<template>
	<div class="schedule-calendar">
		<!-- 现代化顶部栏 -->
		<div class="calendar-header">
			<div class="header-left">
				<!-- 月份显示 -->
				<div class="month-display">
					<div class="month-info">
						<h2 class="current-month">{{ currentMonth }} {{ currentYear }}</h2>
						<div class="today-info">
							<span class="today-label">今天:</span>
							<span class="today-date">{{ todayFormatted }}</span>
						</div>
					</div>
					<div class="nav-controls">
						<button @click="previousMonth" class="nav-btn">
							<span class="nav-text">‹</span>
						</button>
						<button @click="nextMonth" class="nav-btn">
							<span class="nav-text">›</span>
						</button>
					</div>
				</div>
			</div>

			<div class="header-right">
				<!-- 操作按钮组 -->
				<div class="action-buttons">
					<button @click="toggleBatchMode" :class="['action-btn', { active: batchMode }]">
						<span>{{ batchMode ? '退出排班' : '开始排班' }}</span>
					</button>
					<button v-if="batchMode && selectedDateRange" @click="clearBatchSelection" class="action-btn clear-btn">
						<span>清除选择</span>
					</button>
					<button @click="refreshSchedule" :disabled="refreshLoading" class="action-btn" :class="{ loading: refreshLoading }">
						<span v-if="refreshLoading">刷新中...</span>
						<span v-else>刷新</span>
					</button>
					<button @click="legendVisible = true" class="action-btn">
						<span>标识说明</span>
					</button>
				</div>
			</div>
		</div>

		<!-- 标识说明对话框 -->
		<el-dialog v-model="legendVisible" title="标识说明" width="520px" :close-on-click-modal="true">
			<div class="legend-list">
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<i class="cell__side" title="旷工"></i>
					</div>
					<div class="legend-text">左侧红条：旷工</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<i class="corner-flag corner-flag--late" title="迟到"></i>
					</div>
					<div class="legend-text">左上角三角：迟到</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<i class="corner-flag corner-flag--early" title="早退"></i>
					</div>
					<div class="legend-text">右下角三角：早退</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<i class="cell__badge cell__badge--missing" title="缺卡"></i>
					</div>
					<div class="legend-text">左下角小方块：缺卡</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<div class="cell__dots">
							<i class="dot dot--leave" title="请假"></i>
							<i class="dot dot--trip" title="出差"></i>
							<i class="dot dot--ot" title="加班"></i>
						</div>
					</div>
					<div class="legend-text">底部小圆点：请假/出差/加班/调休</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<div class="cell-content">
							<div class="shift-content">
								<div class="abnormal-indicator">
									<span class="abnormal-icon"><i class="fas fa-exclamation-triangle"></i></span>
								</div>
							</div>
						</div>
					</div>
					<div class="legend-text">左上角圆徽：异常排班</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample">
						<div class="cell-content">
							<div class="shift-content">
								<div class="shift-type-indicator" style="color: #059669; background-color: #ecfdf5" title="班次类型">请</div>
							</div>
						</div>
					</div>
					<div class="legend-text">右上角字母徽：班次类型（请假/调休/休班/出差等）</div>
				</div>
				<div class="legend-item">
					<div class="schedule-cell legend-sample is-completed-or-past" style="background: #f3f4f6; opacity: 0.75">
						<div class="cell-content">
							<div class="shift-content">
								<div class="shift-main" style="filter: grayscale(0.4)">
									<div class="shift-name" style="color: #6b7280; font-size: 13px">早班</div>
								</div>
							</div>
						</div>
					</div>
					<div class="legend-text">灰色单元格：已计算或已上班的班次</div>
				</div>
			</div>
			<template #footer>
				<span class="dialog-footer">
					<el-button @click="legendVisible = false">知道了</el-button>
				</span>
			</template>
		</el-dialog>

		<!-- Element Plus Table 排班表 -->
		<div class="schedule-table-container" ref="scheduleTableContainer">
			<el-table
				ref="scheduleTableRef"
				:data="scheduleData"
				:height="tableHeight"
				row-key="personId"
				:row-class-name="getRowClassName"
				@row-click="handleRowClick"
				:show-header="true"
				:border="false"
				:stripe="false"
				:highlight-current-row="false"
				table-layout="fixed"
				class="schedule-el-table"
				:cell-class-name="getCellClassName"
			>
				<!-- 员工信息列 -->
				<el-table-column prop="personName" label="员工信息" width="240" fixed="left" class-name="employee-column">
					<template #header>
						<div class="employee-header-content">
							<el-checkbox
								v-if="batchMode"
								v-model="selectAllPersons"
								:indeterminate="isIndeterminate"
								@change="handleSelectAllChange"
								class="select-all-checkbox"
								title="全选/取消全选"
							/>
							<span class="header-text">员工/工号</span>
						</div>
					</template>
					<template #default="{ row }">
						<div
							:class="[
								'employee-card',
								'clickable',
								{
									'batch-mode': batchMode,
									'is-selected': batchMode && selectedPersonsForBatch.has(row.personId),
								},
							]"
							@click="handleEmployeeClick(row)"
							:title="batchMode ? '选择人员进行批量排班' : '点击查看个人排班'"
						>
							<el-checkbox
								v-if="batchMode"
								:model-value="selectedPersonsForBatch.has(row.personId)"
								@change="(val: boolean) => handlePersonCheckChange(row, val)"
								@click.stop
								class="person-checkbox"
								:title="selectedPersonsForBatch.has(row.personId) ? '取消选择' : '选择此人员'"
							/>
							<div class="employee-avatar">
								<div class="avatar-placeholder">
									{{ getEmployeeInitials(row.personName) }}
								</div>
							</div>
							<div class="employee-info">
								<div class="employee-name">{{ row.personName }}</div>
								<div class="employee-code">{{ row.personCode }}</div>
							</div>
							<div class="employee-stats">
								<div class="stat-item" :class="{ abnormal: getMonthlyStats(row).actualAbnormalDays > 0 }">
									<span class="stat-label">{{ getMonthlyStats(row).actualAbnormalDays > 0 ? '异常' : '当月' }}</span>
									<span class="stat-value">{{ getMonthlyStats(row).abnormalDays }}</span>
								</div>
							</div>
						</div>
					</template>
				</el-table-column>

				<!-- 动态生成日期列 -->
				<el-table-column
					v-for="day in visibleDays"
					:key="day.date"
					:prop="`schedule_${day.date}`"
					:label="day.date"
					width="120"
					:class-name="getDateColumnClassName(day)"
				>
					<template #header>
						<div
							:class="[
								'date-header-content',
								'date-header-selectable',
								{
									'is-weekend': day.isWeekend,
									'is-current-month': day.isCurrentMonth,
									'is-today': isToday(day.date),
									'is-column-selected': selectedDateColumn === day.date,
									'is-column-hovered': hoveredDateColumn === day.date,
								},
							]"
							@click="handleDateColumnClick(day.date)"
							@dblclick="handleDateColumnDblClick(day.date)"
							@mouseenter="hoveredDateColumn = day.date"
							@mouseleave="hoveredDateColumn = null"
							:title="batchMode ? '双击快速选择该日期所有人员' : '双击快速排班该日期'"
						>
							<div class="date-number">{{ day.dayOfMonth }}</div>
							<div class="date-weekday">{{ day.weekday }}</div>
							<!-- 选中状态指示器 -->
							<div v-if="getDateColumnSelectionCount(day.date) > 0" class="column-selection-badge">
								{{ getDateColumnSelectionCount(day.date) }}
							</div>
						</div>
					</template>
					<template #default="{ row }">
						<div
							:class="[
								'schedule-cell',
								getCellClasses(row, day.date),
								{
									'is-weekend': day.isWeekend,
									'is-current-month': day.isCurrentMonth,
									'is-completed-or-past': isCompletedOrPast(row, day.date),
								},
							]"
							:style="getCellStyle(row, day.date)"
							:data-cell-key="`${row.personId}-${day.date}`"
							@dblclick="handleCellDblClick(row, day.date)"
							@click="handleCellClick(row, day.date)"
							@mousedown="batchMode ? handleBatchCellMouseDown(row, day.date, $event) : null"
							@mouseenter="batchMode ? handleBatchCellMouseEnter(row, day.date) : null"
							@mouseup="batchMode ? handleBatchCellMouseUp(row, day.date) : null"
						>
							<!-- 异常侧边条（旷工） -->
							<i v-if="isAbsent(getScheduleForPerson(row, day.date))" class="cell__side" title="旷工"></i>

							<!-- 角落异常形状：迟到、早退 -->
							<i v-if="hasLate(getScheduleForPerson(row, day.date))" class="corner-flag corner-flag--late" title="迟到"></i>
							<i v-if="hasEarly(getScheduleForPerson(row, day.date))" class="corner-flag corner-flag--early" title="早退"></i>

							<!-- 左下角方块：缺卡 -->
							<i v-if="hasMissing(getScheduleForPerson(row, day.date))" class="cell__badge cell__badge--missing" title="缺卡"></i>

							<!-- 底部状态点：请假/出差/加班/调休（最多3个） -->
							<div v-if="getStatusDots(getScheduleForPerson(row, day.date)).length" class="cell__dots">
								<i
									v-for="dot in getStatusDots(getScheduleForPerson(row, day.date)).slice(0, 3)"
									:key="dot"
									:class="['dot', `dot--${dot}`]"
									:title="dotLabel(dot)"
								/>
							</div>

							<!-- 排班内容 -->
							<div class="cell-content">
								<template v-if="getScheduleForPerson(row, day.date)">
									<div class="shift-content">
										<!-- 异常排班标识 -->
										<div v-if="getScheduleForPerson(row, day.date).isAbnormal" class="abnormal-indicator">
											<!-- <span class="abnormal-icon">
												<i class="fas fa-exclamation-triangle"></i>
											</span> -->
										</div>

										<!-- 班次类型标识 -->
										<div
											v-if="getShiftTypeIndicator(getScheduleForPerson(row, day.date))"
											class="shift-type-indicator"
											:style="{
												color: getShiftTypeIndicator(getScheduleForPerson(row, day.date))?.color,
												backgroundColor: getShiftTypeIndicator(getScheduleForPerson(row, day.date))?.bgColor,
											}"
											:title="getShiftTypeText(getScheduleForPerson(row, day.date))"
										>
											{{ getShiftTypeIndicator(getScheduleForPerson(row, day.date))?.text }}
										</div>

										<!-- 休班使用统一的显示格式 -->
										<template v-if="isRestDay(getScheduleForPerson(row, day.date))">
											<div
												class="shift-main"
												:class="getShiftTypeClass(getScheduleForPerson(row, day.date))"
												:style="getShiftStyle(getScheduleForPerson(row, day.date))"
												@click="showScheduleDetailModal(row, day.date, $event)"
											>
												<!-- 班组名称 -->
												<div class="shift-group" v-if="getScheduleForPerson(row, day.date).AttendanceShiftGroupName">
													{{ getScheduleForPerson(row, day.date).AttendanceShiftGroupName }}
												</div>
												<!-- 班次名称 - 休班时显示 ShiftInfo -->
												<div class="shift-name" v-if="getScheduleForPerson(row, day.date).ShiftInfo">
													{{ getScheduleForPerson(row, day.date).ShiftInfo }}
												</div>
												<div class="shift-name" v-else>休</div>
												<!-- 班次时间 -->
												<div class="shift-time" v-if="getScheduleForPerson(row, day.date).ShiftTime">
													{{ getScheduleForPerson(row, day.date).ShiftTime }}
												</div>
											</div>
										</template>
										<!-- 正常班次显示完整信息 -->
										<template v-else>
											<div
												class="shift-main"
												:class="getShiftTypeClass(getScheduleForPerson(row, day.date))"
												:style="getShiftStyle(getScheduleForPerson(row, day.date))"
												@click="showScheduleDetailModal(row, day.date, $event)"
											>
												<!-- 行政班显示完整信息 -->
												<template v-if="isAdminShift(getScheduleForPerson(row, day.date))">
													<!-- 班组名称 -->
													<div class="shift-group" v-if="getScheduleForPerson(row, day.date).AttendanceShiftGroupName">
														{{ getScheduleForPerson(row, day.date).AttendanceShiftGroupName }}
													</div>
													<!-- 班次名称 - 休班时显示 ShiftInfo，其他情况不显示 -->
													<div
														class="shift-name"
														v-if="isRestDay(getScheduleForPerson(row, day.date)) && getScheduleForPerson(row, day.date).ShiftInfo"
													>
														{{ getScheduleForPerson(row, day.date).ShiftInfo }}
													</div>
													<!-- 班次时间 -->
													<div class="shift-time" v-if="getScheduleForPerson(row, day.date).ShiftTime">
														{{ getScheduleForPerson(row, day.date).ShiftTime }}
													</div>
												</template>
												<!-- 非行政班显示完整信息 -->
												<template v-else>
													<!-- 班组名称 -->
													<div class="shift-group" v-if="getScheduleForPerson(row, day.date).AttendanceShiftGroupName">
														{{ getScheduleForPerson(row, day.date).AttendanceShiftGroupName }}
													</div>
													<!-- 班次名称 - 休班时显示 ShiftInfo，其他情况不显示 -->
													<div
														class="shift-name"
														v-if="isRestDay(getScheduleForPerson(row, day.date)) && getScheduleForPerson(row, day.date).ShiftInfo"
													>
														{{ getScheduleForPerson(row, day.date).ShiftInfo }}
													</div>
													<!-- 如果没有班组名称且是休班，显示班次信息 -->
													<div
														class="shift-info"
														v-if="
															!getScheduleForPerson(row, day.date).AttendanceShiftGroupName &&
															isRestDay(getScheduleForPerson(row, day.date)) &&
															getScheduleForPerson(row, day.date).ShiftInfo
														"
													>
														{{ getScheduleForPerson(row, day.date).ShiftInfo }}
													</div>
												</template>
											</div>
										</template>
									</div>
								</template>
								<template v-else>
									<div class="empty-content">
										<span class="empty-text">无排班</span>
									</div>
								</template>
							</div>
						</div>
					</template>
				</el-table-column>
			</el-table>
		</div>

		<!-- 分页组件 -->
		<div class="pagination-container">
			<el-pagination
				v-model:current-page="currentPage"
				v-model:page-size="pageSize"
				:page-sizes="pageSizes"
				:total="totalCount"
				:background="true"
				:small="isMobile"
				:layout="paginationLayout"
				:hide-on-single-page="false"
				:pager-count="pagerCount"
				@size-change="handleSizeChange"
				@current-change="handleCurrentChange"
				class="schedule-pagination"
			/>
		</div>

		<!-- 加载遮罩 -->
		<div v-if="loading" class="loading-overlay">
			<div class="loading-spinner">
				<div class="spinner-ring"></div>
				<div class="loading-text">加载中...</div>
			</div>
		</div>

		<!-- 批量模式提示 -->
		<Transition name="tips-fade">
			<div v-if="batchMode" class="batch-mode-tips">
				<div class="tips-content">
					<div class="tips-header">
						<div class="tips-title">
							<i class="el-icon-info"></i>
							<span>批量排班模式</span>
						</div>
						<el-button size="small" text @click="exitBatchMode" class="exit-btn">
							<i class="el-icon-close"></i>
						</el-button>
					</div>

					<div class="step-tips">
						<div class="step" :class="{ active: selectedPersonsForBatch.size === 0, completed: selectedPersonsForBatch.size > 0 }">
							<div class="step-indicator">
								<span v-if="selectedPersonsForBatch.size === 0" class="step-number">1</span>
								<i v-else class="el-icon-check step-check"></i>
							</div>
							<div class="step-content">
								<div class="step-text">勾选左侧人员复选框，选择需要排班的人员</div>
								<div v-if="selectedPersonsForBatch.size > 0" class="step-detail">已选择 {{ selectedPersonsForBatch.size }} 人</div>
							</div>
						</div>
						<div class="step" :class="{ active: selectedPersonsForBatch.size > 0 && !selectedDateRange, completed: selectedDateRange }">
							<div class="step-indicator">
								<span v-if="!selectedDateRange" class="step-number">2</span>
								<i v-else class="el-icon-check step-check"></i>
							</div>
							<div class="step-content">
								<div class="step-text">拖拽选择日期区域，或双击日期列头快速选择</div>
								<div v-if="selectedDateRange" class="step-detail">已选择日期区域</div>
							</div>
						</div>
						<div class="step" :class="{ active: selectedDateRange }">
							<div class="step-indicator">
								<span class="step-number">3</span>
							</div>
							<div class="step-content">
								<div class="step-text">在弹出面板中选择考勤组并确认</div>
							</div>
						</div>
					</div>

					<!-- 选择详情 -->
					<Transition name="info-slide">
						<div v-if="selectedPersonsForBatch.size > 0 || selectedDateRange" class="selection-info">
							<div v-if="selectedPersonsForBatch.size > 0" class="info-item">
								<i class="el-icon-user"></i>
								<div class="info-content">
									<span class="label">已选人员</span>
									<span class="value">{{ selectedPersonsForBatch.size }} 人</span>
								</div>
							</div>
							<div v-if="selectedPersonsForBatch.size > 0 && selectedDateRange" class="info-divider"></div>
							<div v-if="selectedDateRange" class="info-item">
								<i class="el-icon-date"></i>
								<div class="info-content">
									<span class="label">选择范围</span>
									<span class="value">{{ getSelectionRangeText() }}</span>
								</div>
							</div>
							<div v-if="selectedDateRange" class="info-divider"></div>
							<div v-if="selectedDateRange" class="info-item">
								<i class="el-icon-tickets"></i>
								<div class="info-content">
									<span class="label">选中单元格</span>
									<span class="value">{{ selectedCells.size }} 个</span>
								</div>
							</div>
						</div>
					</Transition>
				</div>
			</div>
		</Transition>

		<!-- 日期列快速选择确认对话框 -->
		<el-dialog
			v-model="showDateColumnSelectDialog"
			title="快速批量排班"
			width="480px"
			:close-on-click-modal="false"
			:show-close="true"
			class="date-column-select-dialog"
		>
			<div class="dialog-content">
				<div class="selection-preview">
					<div class="preview-icon">
						<el-icon><Calendar /></el-icon>
					</div>
					<div class="preview-info">
						<div class="preview-date">
							<strong>{{ formatDateForDisplay(pendingSelectDate) }}</strong>
						</div>
						<div class="preview-hint">为该日期的人员批量排班</div>
					</div>
				</div>

				<el-divider />

				<div class="selection-options">
					<div class="option-title">选择排班范围：</div>
					<el-radio-group v-model="dateColumnSelectScope" class="scope-radio-group">
						<el-radio value="current-page" class="scope-radio">
							<div class="radio-content">
								<div class="radio-label">当前页人员</div>
								<div class="radio-desc">共 {{ scheduleData.length }} 人</div>
							</div>
						</el-radio>
						<el-radio value="all-pages" class="scope-radio" v-if="totalCount > pageSize">
							<div class="radio-content">
								<div class="radio-label">全部人员</div>
								<div class="radio-desc">共 {{ totalCount }} 人（跨 {{ Math.ceil(totalCount / pageSize) }} 页）</div>
							</div>
						</el-radio>
					</el-radio-group>
				</div>

				<el-alert
					v-if="dateColumnSelectScope === 'all-pages' && totalCount > 100"
					title="提示"
					type="warning"
					:closable="false"
					class="selection-warning"
				>
					选择全部人员将会批量操作 {{ totalCount }} 条数据，请确保排班设置正确。
				</el-alert>
			</div>

			<template #footer>
				<div class="dialog-footer">
					<el-button @click="showDateColumnSelectDialog = false">取消</el-button>
					<el-button type="primary" @click="confirmDateColumnSelection"> 确认选择 </el-button>
				</div>
			</template>
		</el-dialog>

		<!-- 个人排班弹窗 -->
		<PersonalScheduleCalendar
			v-model:visible="showPersonalSchedule"
			:person-info="selectedPersonData"
			@close="handlePersonalScheduleClose"
			@schedule-updated="handlePersonalScheduleUpdated"
		/>

		<!-- 批量排班面板 -->
		<div v-if="showBatchPanel" class="batch-panel-overlay" @click="cancelBatchOperation">
			<div
				class="batch-panel"
				:class="{ dragging: batchPanelDragging }"
				:style="{
					left: `${batchPanelPosition.x}px`,
					top: `${batchPanelPosition.y}px`,
				}"
				@click.stop
			>
				<div class="panel-header draggable-header" @mousedown="onBatchPanelMouseDown">
					<div class="header-content">
						<span class="drag-handle">⋮⋮</span>
						<h3>批量排班设置</h3>
					</div>
					<button @click="closeBatchPanel" class="close-btn">
						<span>×</span>
					</button>
				</div>

				<div class="panel-content">
					<!-- 排班表单 -->
					<el-form :model="batchForm" label-width="120px" class="batch-form">
						<!-- 班次类型 -->
						<el-form-item label="班次类型" required>
							<el-radio-group v-model="batchForm.ShiftType">
								<el-radio
									v-for="type in availableShiftTypes"
									:key="type.value"
									:value="type.value"
									:disabled="hasNoScheduleCells && type.value !== '排班'"
								>
									{{ type.label }}
								</el-radio>
							</el-radio-group>
							<div v-if="hasNoScheduleCells" class="form-tip">
								<el-text type="warning" size="small">
									<el-icon><Warning /></el-icon>
									选中的单元格包含无排班状态，只能设置为排班
								</el-text>
							</div>
						</el-form-item>

						<!-- 换班相关配置 (当选择换班时显示) -->
						<template v-if="batchForm.ShiftType === '换班'">
							<!-- 选择换班人员 -->
							<el-form-item label="换班人员" required>
								<PersonSelector
									v-model="batchForm.SwapPersonId"
									placeholder="请输入工号/姓名查询本部门人员（不能选择自己）"
									:clearable="true"
									:group-id="selectedPersonsGroupId"
									:exclude-person-ids="selectedPersonIds"
									@change="handleSwapPersonChange"
								/>
							</el-form-item>

							<!-- 选择换班记录 -->
							<el-form-item v-if="batchForm.SwapPersonId" label="换班记录" required>
								<el-select
									v-model="batchForm.SwapShiftId"
									placeholder="请选择换班记录"
									class="form-input"
									:loading="swapShiftLoading"
									:disabled="swapShiftOptions.length === 0 && !swapShiftLoading"
								>
									<el-option v-for="option in swapShiftOptions" :key="option.value" :label="option.label" :value="option.value" />
									<el-option v-if="swapShiftOptions.length === 0 && !swapShiftLoading" value="" label="该人员暂无可用的排班记录" disabled />
								</el-select>
							</el-form-item>
						</template>

						<!-- 替班人员 (当选择替班时显示) -->
						<el-form-item v-if="batchForm.ShiftType === '替班'" label="替班人员" required>
							<PersonSelector
								v-model="batchForm.SubstitutePersonId"
								placeholder="请输入工号/姓名查询本部门人员（不能选择自己）"
								:clearable="true"
								:group-id="selectedPersonsGroupId"
								:exclude-person-ids="selectedPersonIds"
							/>
						</el-form-item>

						<!-- 正常上班的备注 (当选择正常上班时显示) -->
						<el-form-item v-if="batchForm.ShiftType !== '排班'" label="备注" required>
							<el-input
								v-model="batchForm.Remark"
								type="textarea"
								:rows="4"
								placeholder="请填写备注原因（必填）"
								maxlength="500"
								show-word-limit
								:clearable="true"
							/>
						</el-form-item>

						<!-- 考勤组选择 -->
						<el-form-item label="考勤组" required v-if="batchForm.ShiftType == '排班'">
							<el-select v-model="selectedAttendanceGroup" placeholder="请选择考勤组" class="form-select" clearable>
								<el-option v-for="group in attendanceGroups" :key="group.id" :label="group.name" :value="group.id" />
							</el-select>
						</el-form-item>

						<!-- 自动计算未来日期 -->
						<el-form-item label="自动计算排班" v-if="batchForm.ShiftType === '排班'">
							<el-switch v-model="batchForm.AutoCalculateFuture" />
						</el-form-item>
					</el-form>
				</div>

				<!-- 操作按钮 -->
				<div class="panel-actions">
					<el-button @click="closeBatchPanel" size="large" :disabled="batchLoading"> 取消 </el-button>
					<el-button type="primary" @click="batchChangeSchedule" size="large" :disabled="batchLoading" :loading="batchLoading">
						{{ batchLoading ? '排班中...' : '确认排班' }}
					</el-button>
				</div>
			</div>
		</div>

		<!-- 排班详细信息悬浮卡片 -->
		<el-card
			v-if="showScheduleDetail"
			class="schedule-detail-card"
			:style="{ left: cardPosition.x + 'px', top: cardPosition.y + 'px', transform: cardTransform }"
			shadow="always"
			ref="scheduleDetailCardRef"
			@click.stop
		>
			<template #header>
				<div class="card-header-content">
					<div class="date-info">
						<span class="date-text">{{ formatDisplayDate(selectedScheduleDetail?.date) }}</span>
						<span class="weekday-text">{{ formatWeekday(selectedScheduleDetail?.date) }}</span>
					</div>
				</div>
			</template>

			<el-descriptions :column="1" size="small" border v-if="selectedScheduleDetail?.schedule && !selectedScheduleDetail.schedule.isRest">
				<!-- 班次类型 - 新增 -->
				<el-descriptions-item label="班次状态" v-if="selectedScheduleDetail.schedule.ShiftType || selectedScheduleDetail.schedule.shiftType">
					<div style="display: flex; align-items: center; gap: 8px">
						<el-tag
							:type="getScheduleDetailTagType(selectedScheduleDetail.schedule.ShiftType || selectedScheduleDetail.schedule.shiftType)"
							size="small"
							round
						>
							{{ selectedScheduleDetail.schedule.ShiftType || selectedScheduleDetail.schedule.shiftType }}
						</el-tag>
						<el-tag v-if="selectedScheduleDetail.schedule.isAbnormal || selectedScheduleDetail.schedule.IsAbnormal" type="danger" size="small">
							异常
						</el-tag>
						<!-- 取消按钮 - 仅在非正常上班状态时显示 -->
						<el-button
							v-if="(selectedScheduleDetail.schedule.ShiftType || selectedScheduleDetail.schedule.shiftType) !== '正常上班'"
							type="danger"
							size="small"
							@click="
								cancelScheduleStatus(selectedScheduleDetail.schedule.ID || selectedScheduleDetail.schedule.Id || selectedScheduleDetail.schedule.id)
							"
							:loading="cancelLoading"
						>
							{{ cancelLoading ? '取消中...' : '取消' }}
						</el-button>
					</div>
				</el-descriptions-item>

				<!-- 班组名称 -->
				<el-descriptions-item label="班组名称" v-if="selectedScheduleDetail.schedule.AttendanceShiftGroupName">
					<el-tag type="primary" size="small" round>
						{{ selectedScheduleDetail.schedule.AttendanceShiftGroupName }}
					</el-tag>
				</el-descriptions-item>

				<!-- 班次信息 - 注释掉 ShiftInfo，只显示完整班组信息 -->
				<el-descriptions-item label="班次信息" v-if="selectedScheduleDetail.schedule.ShiftInfo">
					<el-tag type="info" size="small" round>
						{{ selectedScheduleDetail.schedule.ShiftInfo }}
					</el-tag>
				</el-descriptions-item>

				<!-- 替班人员 - 新增 -->
				<el-descriptions-item
					label="替班人员"
					v-if="selectedScheduleDetail.schedule.SubstitutePersonName || selectedScheduleDetail.schedule.substitutePersonName"
				>
					<el-text size="small" type="warning">
						{{ selectedScheduleDetail.schedule.SubstitutePersonName || selectedScheduleDetail.schedule.substitutePersonName }}
					</el-text>
				</el-descriptions-item>

				<!-- 工作时间 -->
				<el-descriptions-item label="工作时间" v-if="selectedScheduleDetail.schedule.workStartTime && selectedScheduleDetail.schedule.workEndTime">
					<el-text size="small" type="primary">
						{{ formatWorkTime(selectedScheduleDetail.schedule.workStartTime, selectedScheduleDetail.schedule.workEndTime) }}
					</el-text>
				</el-descriptions-item>

				<!-- 考勤结果 - 新增 -->
				<el-descriptions-item
					label="考勤结果"
					v-if="selectedScheduleDetail.schedule.attendanceResult || selectedScheduleDetail.schedule.AttendanceResult"
				>
					<el-tag
						:type="getAttendanceResultTagType(selectedScheduleDetail.schedule.attendanceResult || selectedScheduleDetail.schedule.AttendanceResult)"
						size="small"
					>
						{{ selectedScheduleDetail.schedule.attendanceResult || selectedScheduleDetail.schedule.AttendanceResult }}
					</el-tag>
				</el-descriptions-item>

				<!-- 实际打卡时间 - 新增 -->
				<el-descriptions-item label="实际打卡时间" v-if="selectedScheduleDetail.schedule.Schedule || selectedScheduleDetail.schedule.EndTime">
					<div class="punch-time-info">
						<div v-if="selectedScheduleDetail.schedule.StartTime" class="punch-time-item">
							<el-text size="small" type="success">
								<el-icon><Clock /></el-icon>
								上班打卡：{{ selectedScheduleDetail.schedule.StartTime }}
							</el-text>
						</div>
						<div v-if="selectedScheduleDetail.schedule.EndTime" class="punch-time-item">
							<el-text size="small" type="primary">
								<el-icon><Clock /></el-icon>
								下班打卡：{{ selectedScheduleDetail.schedule.EndTime }}
							</el-text>
						</div>
					</div>
				</el-descriptions-item>

				<!-- 状态 -->
				<el-descriptions-item label="排班状态">
					<el-tag :type="selectedScheduleDetail.schedule.status === '已排班' ? 'success' : 'info'" size="small">
						{{ selectedScheduleDetail.schedule.status || '已排班' }}
					</el-tag>
				</el-descriptions-item>
				<!-- 更新人 -->
				<el-descriptions-item label="更新人" v-if="selectedScheduleDetail.schedule.UpdateBy || selectedScheduleDetail.schedule.updateBy">
					<el-text size="small">
						{{ selectedScheduleDetail.schedule.UpdateBy || selectedScheduleDetail.schedule.updateBy }}
					</el-text>
				</el-descriptions-item>
				<!-- 备注信息 - 新增 -->
				<el-descriptions-item label="备注" v-if="selectedScheduleDetail.schedule.remark || selectedScheduleDetail.schedule.Remark">
					<el-text size="small">
						{{ selectedScheduleDetail.schedule.remark || selectedScheduleDetail.schedule.Remark }}
					</el-text>
				</el-descriptions-item>
			</el-descriptions>

			<el-empty
				v-else
				:description="selectedScheduleDetail?.schedule?.isRest ? '休班' : selectedScheduleDetail?.isWeekend ? '周末休息' : '暂无排班安排'"
				:image-size="60"
			>
				<el-text type="info" size="small">
					{{ selectedScheduleDetail?.schedule?.isRest ? '今日为休班日' : selectedScheduleDetail?.isWeekend ? '今日为休息日' : '该日期暂未安排工作' }}
				</el-text>
			</el-empty>
		</el-card>

		<!-- 点击遮罩关闭卡片 -->
		<div v-if="showScheduleDetail" class="card-overlay" @click="closeScheduleDetail"></div>
	</div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { ElMessage, ElLoading } from 'element-plus';
import { Clock, Warning, Calendar } from '@element-plus/icons-vue';
import PersonalScheduleCalendar from '../personalScheduleCalendar/index.vue';
import PersonSelector from '/@/components/personSelector/index.vue';
import { SysAttendanceRecordApi as attendanceApi } from '/@/views/hr/sysattendancerecord/api';
import { SysAttendanceShiftGroupApi as attendanceGroupApi } from '/@/views/hr/sysattendanceshiftgroup/api';
import type { ISearchByPersonRequest } from '/@/views/hr/sysattendancerecord/api/interface';
import { formatDate } from '/@/utils/formatTime';

// 状态与异常判定（用于UI标识）
const legendVisible = ref(false);

const isAbsent = (schedule: any) => {
	if (!schedule) return false;
	const result = schedule.attendanceResult || '';
	return result === '旷工';
};

const hasLate = (schedule: any) => {
	if (!schedule) return false;
	const result = schedule.attendanceResult || '';
	return result.includes('迟到');
};

const hasEarly = (schedule: any) => {
	if (!schedule) return false;
	const result = schedule.attendanceResult || '';
	return result.includes('早退');
};

const hasMissing = (schedule: any) => {
	if (!schedule) return false;
	const result = schedule.attendanceResult || '';
	return result.includes('缺卡');
};

// 状态点：请假/出差/加班/调休
const getStatusDots = (schedule: any): string[] => {
	if (!schedule) return [];
	const shiftType = schedule.ShiftType || schedule.shiftType || '';
	const dots: string[] = [];
	if (shiftType.includes('请假')) dots.push('leave');
	if (shiftType.includes('出差')) dots.push('trip');
	if (shiftType.includes('加班')) dots.push('ot');
	if (shiftType.includes('调休')) dots.push('offinlieu');
	if (shiftType.includes('替班')) dots.push('substitute');
	if (shiftType.includes('换班')) dots.push('shift-change');
	return dots;
};

const dotLabel = (dot: string) => {
	switch (dot) {
		case 'leave':
			return '请假';
		case 'trip':
			return '出差';
		case 'ot':
			return '加班';
		case 'offinlieu':
			return '调休';
		case 'substitute':
			return '替班';
		case 'swap':
			return '换班';
		default:
			return '';
	}
};
// 定义接口类型
interface IScheduleData {
	ID: string;
	Name: string;
	StaffNo: string;
	GroupId?: string; // 部门ID
	groupId?: string; // 部门ID（小写）
	DeptId?: string; // 部门ID（备用字段名）
	deptId?: string; // 部门ID（小写备用字段名）
	Records: Array<{
		AttendanceShiftGroupId?: string;
		AttendanceShiftGroupName?: string;
		PersonName?: string;
		ShiftDate?: string;
		ShiftName?: string;
		ShiftInfo?: string;
		ShiftType?: string;
		WorkStartTime?: string;
		WorkEndTime?: string;
		Status?: string;
		AttendanceResult?: string;
		ScheduleType?: string;
		IsAbnormal?: boolean;
		DisplayColor?: string;
	}>;
}

// 数据验证函数
const validateScheduleData = (data: any[]): boolean => {
	if (!Array.isArray(data)) {
		return false;
	}

	let validCount = 0;
	let invalidCount = 0;

	data.forEach((item, index) => {
		if (!item) {
			invalidCount++;
			return;
		}

		// 检查必要字段
		const hasId = item.ID || item.PersonId || item.id;
		const hasName = item.Name || item.PersonName || item.name;

		if (hasId && hasName) {
			validCount++;
		} else {
			invalidCount++;
		}
	});

	return validCount > 0;
};

// Props
interface Props {
	data: IScheduleData[];
	loading?: boolean;
	selectedDeptId?: string; // 当前选中的部门ID，用于查询替班人员
	total?: number; // 总记录数（用于后端分页）
}

const props = withDefaults(defineProps<Props>(), {
	loading: false,
	selectedDeptId: '',
	total: 0,
});

// Emits
const emit = defineEmits<{
	'cell-click': [data: any];
	'cell-dblclick': [data: any];
	'schedule-updated': [data: any];
	'date-change': [data: { year: number; month: number }];
	'refresh-data': [];
	'page-change': [data: { page: number; limit: number }];
}>();

// 组件引用
const scheduleTableContainer = ref<HTMLElement | null>(null);
const scheduleTableRef = ref<any>(null);

// 响应式数据
const currentDate = ref(new Date());
// 个人排班弹窗相关状态
const showPersonalSchedule = ref(false);
// 部门人员列表（用于替班人员选择）
const deptPersons = ref<any[]>([]);
const selectedPersonData = ref<any>(null);

// 分页相关状态
const currentPage = ref(1);
const pageSize = ref(50);
const pageSizes = ref([10, 20, 50, 100, 200]);
const totalCount = ref(0);

// 响应式检测
const isMobile = computed(() => {
	if (typeof window === 'undefined') return false;
	return window.innerWidth <= 767;
});

const isTablet = computed(() => {
	if (typeof window === 'undefined') return false;
	return window.innerWidth > 767 && window.innerWidth <= 991;
});

// 响应式分页配置
const paginationLayout = computed(() => {
	if (isMobile.value) {
		return 'total, prev, pager, next';
	} else if (isTablet.value) {
		return 'total, sizes, prev, pager, next';
	} else {
		return 'total, sizes, prev, pager, next, jumper';
	}
});

const pagerCount = computed(() => {
	if (isMobile.value) {
		return 5;
	} else if (isTablet.value) {
		return 7;
	} else {
		return 9;
	}
});
const allScheduleData = ref<any[]>([]); // 存储所有数据
const fullLoadedPersons = ref<any[]>([]); // 存储完整加载的所有人员数据（用于批量排班）

// 批量排班相关状态
const batchMode = ref(false);
const selectedPersons = ref<Set<string>>(new Set());
const selectedDateRange = ref<{ start: string; end: string } | null>(null);
const selectedPersonsForBatch = ref<Set<string>>(new Set()); // 用于复选框选择的人员集合
const selectAllPersons = ref(false); // 全选状态
const isIndeterminate = ref(false); // 半选状态

// 刷新按钮加载状态
const refreshLoading = ref(false);
const isRangeSelecting = ref(false);
const rangeStartDate = ref<string>('');
const rangeStartPerson = ref<any>(null); // 添加起始员工
const selectedCells = ref<Set<string>>(new Set()); // 存储选中的单元格 (person.id + '_' + date)
const showBatchPanel = ref(false);
const selectedAttendanceGroup = ref<string>('');
const batchLoading = ref(false);

// 日期列快速选择相关状态
const selectedDateColumn = ref<string | null>(null); // 当前选中的日期列
const hoveredDateColumn = ref<string | null>(null); // 当前悬浮的日期列
const showDateColumnSelectDialog = ref(false); // 显示日期列选择对话框
const pendingSelectDate = ref<string>(''); // 待选择的日期
const dateColumnSelectScope = ref<'current-page' | 'all-pages'>('current-page'); // 选择范围：当前页或全部

// 检查选中的单元格是否包含"无排班"状态
const hasNoScheduleCells = computed(() => {
	if (selectedCells.value.size === 0) return false;

	// 遍历选中的单元格，检查是否有"无排班"状态
	for (const cellKey of selectedCells.value) {
		const [personId, date] = cellKey.split('_');
		const person = scheduleData.value.find((p: any) => p.personId === personId);
		if (person) {
			const schedule = getScheduleForPerson(person, date);
			if (!schedule) {
				// 无排班状态
				return true;
			}
		}
	}
	return false;
});

// 检查选中的单元格是否全部为考勤异常
const hasAbnormalCells = computed(() => {
	if (selectedCells.value.size === 0) return false;

	let abnormalCount = 0;
	let totalCount = 0;

	// 遍历选中的单元格，检查是否有考勤异常
	for (const cellKey of selectedCells.value) {
		const [personId, date] = cellKey.split('_');
		const person = scheduleData.value.find((p: any) => p.personId === personId);
		if (person) {
			const schedule = getScheduleForPerson(person, date);
			if (schedule) {
				totalCount++;
				if (schedule.isAbnormal) {
					abnormalCount++;
				}
			}
		}
	}

	// 只有当全部选中的单元格都是考勤异常时才返回true
	return totalCount > 0 && abnormalCount === totalCount;
});

// 可选择的班次类型（基于选中单元格状态）
const availableShiftTypes = computed(() => {
	const allTypes = [
		{ label: '排班', value: '排班' },
		{ label: '请假', value: '请假' },
		{ label: '调休', value: '调休' },
		{ label: '替班', value: '替班' },
		{ label: '换班', value: '换班' },
	];

	// 如果选中的单元格包含"无排班"状态，只能选择"排班"
	if (hasNoScheduleCells.value) {
		return [{ label: '排班', value: '排班' }];
	}

	// 如果选中的单元格全部为考勤异常，只显示"正常上班"选项
	if (hasAbnormalCells.value) {
		return [{ label: '正常上班', value: '正常上班' }];
	}

	return allTypes;
});

// 批量排班表单数据（必须在 watch 之前定义）
const batchForm = ref({
	ShiftType: '排班', // 班次类型
	SwapShiftId: '', // 调班ID
	SwapPersonId: '', // 换班人员ID
	SubstitutePersonId: '', // 代班人员ID
	DateRange: null as [string, string] | null, // 日期范围
	AutoCalculateFuture: false, // 自动计算未来日期
	Remark: '', // 正常上班的备注原因
});

// 监听无排班状态变化，自动重置班次类型
watch(hasNoScheduleCells, (hasNoSchedule) => {
	if (hasNoSchedule && batchForm.value.ShiftType !== '排班') {
		batchForm.value.ShiftType = '排班';
		ElMessage.warning('检测到选中单元格包含无排班状态，已自动切换为排班');
	}
});

// 监听异常状态变化，自动切换到正常上班
watch(hasAbnormalCells, (hasAbnormal) => {
	if (hasAbnormal && batchForm.value.ShiftType !== '正常上班') {
		batchForm.value.ShiftType = '正常上班';
		ElMessage.warning('检测到选中单元格包含异常状态，已自动切换为正常上班');
	}
});

// 监听班次类型变化，清除不相关字段
watch(
	() => batchForm.value.ShiftType,
	(newType) => {
		// 如果不是正常上班，清除Remark字段
		if (newType !== '正常上班') {
			batchForm.value.Remark = '';
		}
	}
);

// 批量排班面板拖拽相关状态
const batchPanelPosition = ref({ x: 0, y: 0 });
const batchPanelDragging = ref(false);
const batchPanelDragStart = ref({ x: 0, y: 0 });

// 监听批量排班面板显示状态，确保位置正确
watch(showBatchPanel, (isShown) => {
	if (isShown && !batchPanelDragging.value) {
		// 延迟计算以确保DOM已渲染
		nextTick(() => {
			batchPanelPosition.value = calculateCenterPosition();
		});
	}
});

// 换班相关数据
const swapShiftOptions = ref<Array<{ value: string; label: string }>>([]);
const swapShiftLoading = ref(false);

// 计算选中人员的部门ID（用于替班和换班人员过滤）
const selectedPersonsGroupId = computed(() => {
	// 优先使用父组件传递的部门ID
	if (props.selectedDeptId) {
		return props.selectedDeptId;
	}

	// 如果没有选中单元格，返回空字符串
	if (selectedCells.value.size === 0) {
		return '';
	}

	// 获取第一个选中单元格的人员ID
	const firstCellKey = Array.from(selectedCells.value)[0];
	const [personId] = firstCellKey.split('_');

	// 从原始数据中查找该人员的部门ID
	const personData = props.data.find((p: any) => p.ID === personId || p.PersonId === personId || p.personId === personId);

	// 尝试从多种可能的字段获取部门ID
	let groupId = personData?.GroupId || personData?.groupId || personData?.DeptId || personData?.deptId;

	// 如果没有，从 Records 数组中获取考勤组ID作为备选
	if (!groupId && personData?.Records && personData.Records.length > 0) {
		const firstRecord: any = personData.Records[0];
		groupId = firstRecord?.AttendanceShiftGroupId || firstRecord?.GroupId || firstRecord?.groupId;
	}

	return groupId || '';
});

// 计算选中人员的ID列表（用于排除自己）
const selectedPersonIds = computed(() => {
	if (selectedCells.value.size === 0) return [];

	const personIds = new Set<string>();
	selectedCells.value.forEach((cellKey) => {
		const [personId] = cellKey.split('_');
		personIds.add(personId);
	});

	return Array.from(personIds);
});

// 考勤组数据（从班组API获取）
const attendanceGroups = ref<Array<{ id: string; name: string }>>([]);

// 详情显示相关
const showScheduleDetail = ref(false);
const selectedScheduleDetail = ref<any>(null);
const cardPosition = ref({ x: 0, y: 0 });
const cardTransform = ref('translate(-50%, 0)');
const scheduleDetailCardRef = ref();
const cancelLoading = ref(false);

// 班次拖拽状态已移除

// 使用项目现有的日期格式化函数
const formatDateToYYYYMMDD = (date: Date): string => {
	try {
		if (!date || !(date instanceof Date)) return '';
		return formatDate(date, 'YYYY-mm-dd');
	} catch (error) {
		// Error formatting date
		return '';
	}
};

// 解析日期字符串为Date对象
const parseDateString = (dateStr: string): Date | null => {
	try {
		if (!dateStr) return null;
		// 确保日期字符串格式正确 (yyyy-MM-dd)
		const [year, month, day] = dateStr.split('-');
		if (!year || !month || !day) return null;
		return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
	} catch (error) {
		// Error parsing date string
		return null;
	}
};

// 计算属性
const currentYear = computed(() => {
	try {
		return currentDate.value?.getFullYear() || new Date().getFullYear();
	} catch (error) {
		// Error getting current year
		return new Date().getFullYear();
	}
});

const currentMonth = computed(() => {
	try {
		const month = currentDate.value?.getMonth() + 1 || new Date().getMonth() + 1;
		return `${month}月`;
	} catch (error) {
		// Error getting current month
		return `${new Date().getMonth() + 1}月`;
	}
});

// 今天日期格式化显示
const todayFormatted = computed(() => {
	try {
		const today = new Date();
		const month = today.getMonth() + 1;
		const date = today.getDate();
		const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
		const weekday = weekdays[today.getDay()];
		return `${month}月${date}日 ${weekday}`;
	} catch (error) {
		// Error formatting today date
		return '今天';
	}
});

// 处理所有数据（不分页）
const processAllScheduleData = computed(() => {
	try {
		// 从传入的数据中提取人员信息
		if (!props.data || !Array.isArray(props.data)) {
			return [];
		}

		// 验证数据格式
		if (!validateScheduleData(props.data)) {
			// Data validation failed, skip processing
			return [];
		}

		const processedData = props.data
			.map((person) => {
				if (!person) return null;
				return {
					personId: person.ID || `person_${Math.random()}`,
					personName: person.Name || '未知员工',
					personCode: person.StaffNo || '',
					records: person.Records || [],
				};
			})
			.filter(Boolean); // 过滤掉null值

		// 更新总数：优先使用父组件传入的总数（后端分页），否则使用当前数据长度（前端分页）
		totalCount.value = props.total > 0 ? props.total : processedData.length;
		allScheduleData.value = processedData;

		return processedData;
	} catch (error) {
		// Error processing schedule data
		return [];
	}
});

// 当前页显示的数据（后端分页，直接使用父组件传入的数据）
const scheduleData = computed(() => {
	try {
		const allData = processAllScheduleData.value;
		return allData;
	} catch (error) {
		console.error('Error getting schedule data:', error);
		return [];
	}
});

// 加载部门人员（用于替班人员选择）
const loadDeptPersons = async (deptId?: string) => {
	if (!deptId) {
		deptPersons.value = [];
		return;
	}

	try {
		const api = attendanceApi();
		const params = {
			GroupId: deptId,
			Page: 1,
			Limit: 200, // 获取更多人员用于选择
			IsPlainText: true,
			IsEmunToString: true,
		};

		const res = await api.searchByPerson(params);
		if (res.data && Array.isArray(res.data)) {
			deptPersons.value = res.data.map((person: any) => ({
				id: person.ID || person.id || person.PersonId,
				name: person.Name || person.name || person.PersonName || '未知员工',
			}));
		} else {
			deptPersons.value = [];
		}
	} catch (error) {
		console.error('加载部门人员失败:', error);
		deptPersons.value = [];
	}
};

// 所有人员数据（优先使用部门人员，否则使用排班数据中的人员）
const allPersons = computed(() => {
	try {
		// 如果有部门人员数据，优先使用
		if (deptPersons.value.length > 0) {
			return deptPersons.value;
		}

		// 否则使用排班数据中的人员信息作为备选
		const schedulePersons = processAllScheduleData.value.map((person: any) => ({
			id: person.personId,
			name: person.personName,
		}));

		return schedulePersons;
	} catch (error) {
		console.error('获取人员数据失败:', error);
		return [];
	}
});

const visibleDays = computed(() => {
	try {
		const days = [];
		const year = currentDate.value?.getFullYear() || new Date().getFullYear();
		const month = currentDate.value?.getMonth() || new Date().getMonth();
		const firstDay = new Date(year, month, 1);
		const lastDay = new Date(year, month + 1, 0);
		const daysInMonth = lastDay.getDate();

		// 只生成当前月份的天数
		for (let i = 1; i <= daysInMonth; i++) {
			const date = new Date(year, month, i);

			// 使用统一的日期格式化函数
			const localDateStr = formatDateToYYYYMMDD(date);

			days.push({
				date: localDateStr,
				dayOfMonth: date.getDate(),
				weekday: ['日', '一', '二', '三', '四', '五', '六'][date.getDay()],
				isCurrentMonth: true, // 所有日期都是当前月份
				isWeekend: date.getDay() === 0 || date.getDay() === 6,
			});
		}

		return days;
	} catch (error) {
		// Error generating visible days
		return [];
	}
});

// 方法
const getScheduleForPerson = (person: any, date: string) => {
	// 查找该人员在该日期的排班记录
	if (!person.records || !Array.isArray(person.records) || person.records.length === 0) {
		// Records为空时返回null，显示"无排班"
		return null;
	}

	// 解析目标日期字符串 (格式: YYYY-MM-DD)
	const targetDateStr = date; // 保持原格式用于比较

	// 查找该日期的所有排班记录
	const records = person.records.filter((r: any) => {
		if (!r) return false;

		// 支持多种可能的日期字段名
		const recordDate = r.ShiftDate || r.Date || r.date || r.shiftDate || r.WorkDate || r.workDate;

		if (!recordDate) {
			return false;
		}

		// 处理不同的日期格式
		let recordDateStr;
		if (typeof recordDate === 'string') {
			// 如果是字符串，提取日期部分 (YYYY-MM-DD)
			recordDateStr = recordDate.split('T')[0]; // 去掉时间部分
		} else if (recordDate instanceof Date) {
			// 如果是Date对象，格式化为YYYY-MM-DD
			recordDateStr = formatDateToYYYYMMDD(recordDate);
		} else {
			return false;
		}

		const isMatch = recordDateStr === targetDateStr;
		return isMatch;
	});

	// 如果有多条记录，优先取第一条或根据优先级选择
	const record = records.length > 0 ? records[0] : null;

	if (record) {
		// 根据实际数据结构映射字段
		const result = {
			// 基础信息
			ID: record.ID || record.Id || record.id || '', // 添加排班记录ID映射
			shiftId: record.AttendanceShiftId || record.attendanceShiftId || record.ShiftId || record.shiftId,
			// 优先使用完整班组信息，不再使用 ShiftInfo
			shiftName:
				record.AttendanceShiftGroupName ||
				record.attendanceShiftGroupName ||
				record.ShiftName ||
				record.shiftName ||
				(record.AttendanceShift && record.AttendanceShift.Name) ||
				(record.attendanceShift && record.attendanceShift.name) ||
				// record.ShiftInfo || // 注释掉 ShiftInfo
				// record.shiftInfo || // 注释掉 ShiftInfo
				'排班',

			// 人员信息
			personId: record.PersonId || record.personId || '',
			personName: record.PersonName || record.personName || '',

			// 时间信息
			StartTime:
				record.StartTime ||
				record.startTime ||
				record.WorkStartTime ||
				record.workStartTime ||
				(record.AttendanceShift && record.AttendanceShift.StartTime) ||
				'',
			EndTime:
				record.EndTime ||
				record.endTime ||
				record.WorkEndTime ||
				record.workEndTime ||
				(record.AttendanceShift && record.AttendanceShift.EndTime) ||
				'',
			workStartTime: record.WorkStartTime || record.workStartTime || '',
			workEndTime: record.WorkEndTime || record.workEndTime || '',

			// 状态信息
			status: record.Status || record.status || '已排班',
			attendanceResult: record.AttendanceResult || record.attendanceResult || '',
			scheduleType: record.ScheduleType || record.scheduleType || '',
			isAbnormal: record.IsAbnormal || record.isAbnormal || false,
			updateBy: record.UpdateBy || record.updateBy || '',

			// 外观设置
			DisplayColor: record.DisplayColor || record.displayColor || '',

			// 详细信息
			shiftInfo: record.ShiftInfo || record.shiftInfo || '',
			shiftType: record.ShiftType || record.shiftType || '',
			shiftDate: record.ShiftDate || record.shiftDate || '',
			attendanceShiftName: record.AttendanceShiftName || record.attendanceShiftName || '',
			AttendanceShiftGroupName: record.AttendanceShiftGroupName || record.attendanceShiftGroupName || '',
			AttendanceShiftGroupId: record.AttendanceShiftGroupId || record.attendanceShiftGroupId || '', // 添加考勤组ID映射
			lateMinutes: record.LateMinutes || record.lateMinutes || 0,
			earlyLeaveMinutes: record.EarlyLeaveMinutes || record.earlyLeaveMinutes || 0,
			workMinutes: record.WorkMinutes || record.workMinutes || 0,
			missingPunchCount: record.MissingPunchCount || record.missingPunchCount || 0,
			remark: record.Remark || record.remark || '',

			// 保持向后兼容的字段
			ShiftInfo: record.ShiftInfo || record.shiftInfo || '', // 添加大写的ShiftInfo字段
			ShiftType: record.ShiftType || record.shiftType || '',
			ShiftDate: record.ShiftDate || record.shiftDate || '',
			ScheduleType: record.ScheduleType || record.scheduleType || '',

			// 新增字段 - 替班人员（班组名称已在上面定义过了）
			SubstitutePersonName: record.SubstitutePersonName || record.substitutePersonName || '',
		};

		return result;
	}

	// 如果Records不为空但没有找到该日期的记录，也返回null显示"无排班"
	return null;
};

// 判断是否为休班
const isRestDay = (schedule: any) => {
	if (!schedule) return false;

	// 检查班次信息是否包含"休"字
	const shiftInfo = schedule.ShiftInfo || schedule.shiftInfo || '';
	const shiftName = schedule.shiftName || '';
	const shiftType = schedule.shiftType || schedule.ShiftType || '';

	// 判断休班的条件
	return (
		shiftInfo === '休' ||
		shiftInfo.includes('休班') ||
		shiftName === '休' ||
		shiftName.includes('休班') ||
		shiftType === '休' ||
		shiftType.includes('休班')
	);
};

// 判断是否为行政班
const isAdminShift = (schedule: any) => {
	if (!schedule) return false;

	const shiftInfo = schedule.ShiftInfo || schedule.shiftInfo || '';
	const shiftName = schedule.shiftName || '';
	const groupName = schedule.AttendanceShiftGroupName || schedule.attendanceShiftGroupName || '';

	// 判断行政班的条件
	return shiftInfo.includes('行政班') || shiftName.includes('行政班') || groupName.includes('行政班');
};

// 获取班次类型的样式类
const getShiftTypeClass = (schedule: any) => {
	if (!schedule) return '';

	const shiftType = schedule.ShiftType || schedule.shiftType || '';
	const isAbnormal = schedule.isAbnormal || schedule.IsAbnormal || false;

	const classes = [];

	// 异常排班标识
	if (isAbnormal) {
		classes.push('abnormal-schedule');
	}

	// 基于班次类型的样式
	switch (shiftType) {
		case '排班':
		case '正常上班':
			classes.push('normal-work');
			break;
		case '请假':
			classes.push('leave');
			break;
		case '调休':
			classes.push('compensatory-leave');
			break;
		case '替班':
			classes.push('substitute');
			break;
		case '换班':
			classes.push('shift-change');
			break;
		case '加班':
			classes.push('overtime');
			break;
		default:
			if (isRestDay(schedule)) {
				classes.push('rest-day');
			} else {
				classes.push('other-type');
			}
			break;
	}

	return classes.join(' ');
};

// 获取班次类型显示文字
const getShiftTypeText = (schedule: any) => {
	if (!schedule) return '';

	const shiftType = schedule.ShiftType || schedule.shiftType || '';

	// 如果有明确的班次类型，直接返回
	if (shiftType && shiftType !== '排班') {
		return shiftType;
	}

	// 如果是休班
	if (isRestDay(schedule)) {
		return '休班';
	}

	// 默认返回空，不显示类型标签
	return '';
};

// 获取班次类型标识信息
const getShiftTypeIndicator = (schedule: any) => {
	if (!schedule) return null;

	const shiftType = schedule.ShiftType || schedule.shiftType || '';

	// 排班和正常上班不显示标识
	if (!shiftType || shiftType === '排班' || shiftType === '正常上班') {
		return null;
	}

	// 根据班次类型返回标识信息
	const indicators: Record<string, { text: string; color: string; bgColor: string }> = {
		请假: { text: '假', color: '#16a34a', bgColor: '#f0fdf4' },
		调休: { text: '休', color: '#059669', bgColor: '#ecfdf5' },
		替班: { text: '替', color: '#0891b2', bgColor: '#f0f9ff' },
		换班: { text: '换', color: '#7c3aed', bgColor: '#faf5ff' },
		加班: { text: '加', color: '#dc2626', bgColor: '#fef2f2' },
		休班: { text: '休班', color: '#ca8a04', bgColor: '#fffbeb' },
		出差: { text: '差', color: '#0891b2', bgColor: '#f0f9ff' },
	};

	// 如果是休班但ShiftType没有明确标识
	if (isRestDay(schedule) && !indicators[shiftType]) {
		return indicators['休班'];
	}

	return indicators[shiftType] || { text: '其', color: '#64748b', bgColor: '#f8fafc' };
};

// 获取班次的内联样式
const getShiftStyle = (schedule: any) => {
	if (!schedule) return {};

	// 如果有DisplayColor，降低饱和度后设置背景色
	if (schedule.DisplayColor) {
		const softColor = reduceSaturation(schedule.DisplayColor, 0.6);
		return {
			backgroundColor: softColor,
			color: getContrastColor(softColor),
		};
	}

	return {};
};

// 降低颜色饱和度，使其更柔和
const reduceSaturation = (hexColor: string, saturationFactor: number = 0.6) => {
	if (!hexColor) return hexColor;

	// 移除#号
	const color = hexColor.replace('#', '');

	// 转换为RGB
	let r = parseInt(color.substr(0, 2), 16);
	let g = parseInt(color.substr(2, 2), 16);
	let b = parseInt(color.substr(4, 2), 16);

	// 转换为HSL
	r /= 255;
	g /= 255;
	b /= 255;

	const max = Math.max(r, g, b);
	const min = Math.min(r, g, b);
	let h = 0,
		s = 0,
		l = (max + min) / 2;

	if (max !== min) {
		const d = max - min;
		s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

		switch (max) {
			case r:
				h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
				break;
			case g:
				h = ((b - r) / d + 2) / 6;
				break;
			case b:
				h = ((r - g) / d + 4) / 6;
				break;
		}
	}

	// 降低饱和度
	s *= saturationFactor;

	// 提高亮度
	l = Math.min(l * 1.15, 0.95);

	// 转换回RGB
	const hue2rgb = (p: number, q: number, t: number) => {
		if (t < 0) t += 1;
		if (t > 1) t -= 1;
		if (t < 1 / 6) return p + (q - p) * 6 * t;
		if (t < 1 / 2) return q;
		if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
		return p;
	};

	let newR, newG, newB;
	if (s === 0) {
		newR = newG = newB = l;
	} else {
		const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
		const p = 2 * l - q;
		newR = hue2rgb(p, q, h + 1 / 3);
		newG = hue2rgb(p, q, h);
		newB = hue2rgb(p, q, h - 1 / 3);
	}

	// 转换回16进制
	const toHex = (n: number) => {
		const hex = Math.round(n * 255).toString(16);
		return hex.length === 1 ? '0' + hex : hex;
	};

	return '#' + toHex(newR) + toHex(newG) + toHex(newB);
};

// 根据背景色获取合适的文字颜色
const getContrastColor = (hexColor: string) => {
	if (!hexColor) return '#333333';

	// 移除#号
	const color = hexColor.replace('#', '');

	// 转换为RGB
	const r = parseInt(color.substr(0, 2), 16);
	const g = parseInt(color.substr(2, 2), 16);
	const b = parseInt(color.substr(4, 2), 16);

	// 计算亮度
	const brightness = (r * 299 + g * 587 + b * 114) / 1000;

	// 根据亮度返回黑色或白色文字
	return brightness > 128 ? '#333333' : '#ffffff';
};

// 格式化工作时间
const formatWorkTime = (startTime: string, endTime: string) => {
	if (!startTime || !endTime) {
		return '';
	}

	// 处理时间格式，提取HH:mm部分
	const formatTime = (time: string) => {
		if (!time) return '';

		// 如果是完整的日期时间字符串，提取时间部分
		if (time.includes('T')) {
			time = time.split('T')[1];
		}

		// 如果包含秒数，去掉秒数部分
		if (time.includes(':') && time.split(':').length >= 2) {
			const parts = time.split(':');
			return `${parts[0]}:${parts[1]}`;
		}

		return time;
	};

	const formattedStart = formatTime(startTime);
	const formattedEnd = formatTime(endTime);

	if (!formattedStart || !formattedEnd) {
		return '';
	}

	return `${formattedStart}-${formattedEnd}`;
};

// 显示排班详情
const showScheduleDetailModal = (person: any, date: string, event?: Event) => {
	// 在批量排班模式下，不显示详情弹窗
	if (batchMode.value) {
		return;
	}

	const scheduleData = getScheduleForPerson(person, date);

	showScheduleDetail.value = true;
	selectedScheduleDetail.value = {
		person,
		date,
		schedule: scheduleData,
		isWeekend: isWeekend(new Date(date)),
	};

	// 记录点击位置用于卡片初始定位
	const clickTarget = event?.target as HTMLElement;
	if (clickTarget) {
		const rect = clickTarget.getBoundingClientRect();
		cardPosition.value = {
			x: rect.left + rect.width / 2,
			y: rect.bottom + 8,
		};
		cardTransform.value = 'translate(-50%, 0)';
	}

	// 下一帧计算防溢出位置
	nextTick(() => {
		const cardEl = scheduleDetailCardRef.value?.$el || scheduleDetailCardRef.value;
		if (!cardEl) return;
		const cardRect = cardEl.getBoundingClientRect();
		const vw = window.innerWidth;
		const vh = window.innerHeight;
		const margin = 12;

		let x = cardPosition.value.x;
		let y = cardPosition.value.y;

		// 水平居中并限制左右边界
		const halfWidth = cardRect.width / 2;
		x = Math.max(margin + halfWidth, Math.min(vw - margin - halfWidth, x));

		// 垂直方向：优先显示在目标下方，不够则翻转到上方
		const spaceBelow = vh - cardPosition.value.y - margin;
		const spaceAbove = (event && (event as MouseEvent).clientY ? (event as MouseEvent).clientY : cardPosition.value.y) - margin;
		if (spaceBelow < cardRect.height && spaceAbove > cardRect.height) {
			// 放到上方
			y = (clickTarget ? clickTarget.getBoundingClientRect().top : cardPosition.value.y) - 8;
			cardTransform.value = 'translate(-50%, -100%)';
		} else {
			// 保持在下方
			y = cardPosition.value.y;
			cardTransform.value = 'translate(-50%, 0)';
		}

		// 应用修正
		cardPosition.value = { x, y };
	});
};

// 关闭排班详情弹窗
const closeScheduleDetail = () => {
	showScheduleDetail.value = false;
	selectedScheduleDetail.value = null;
};

// 判断是否是周末
const isWeekend = (date: Date): boolean => {
	const day = date.getDay();
	return day === 0 || day === 6;
};

// 格式化显示日期
const formatDisplayDate = (dateStr: string): string => {
	if (!dateStr) return '';
	const date = new Date(dateStr);
	return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
};

// 格式化星期
const formatWeekday = (dateStr: string): string => {
	if (!dateStr) return '';
	const date = new Date(dateStr);
	const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
	return weekdays[date.getDay()];
};

// 取消排班状态
const cancelScheduleStatus = async (scheduleId: string) => {
	if (!selectedScheduleDetail.value) {
		ElMessage.error('未选择排班记录');
		return;
	}

	try {
		cancelLoading.value = true;

		const { person, date, schedule } = selectedScheduleDetail.value;

		// 使用batchChange接口取消特殊状态（如请假、调休等），恢复为正常排班
		// 使用当前排班记录的考勤组ID
		await attendanceApi().batchChange({
			PersonIds: [person.ID || person.PersonId || person.id || person.personId],
			DateRange: [date, date], // 使用数组格式，开始和结束日期相同
			AttendanceShiftGroupId: schedule.AttendanceShiftGroupId || '', // 使用当前考勤组ID
			ShiftType: '排班', // 恢复为正常排班状态
			AutoCalculateFuture: false, // 自动计算未来日期
		});

		ElMessage.success('排班状态已取消');

		// 刷新排班数据
		await refreshSchedule();

		// 关闭详情弹窗
		closeScheduleDetail();
	} catch (error) {
		console.error('取消排班状态失败:', error);
		ElMessage.error('取消排班状态失败，请重试');
	} finally {
		cancelLoading.value = false;
	}
};

// 获取班次类型对应的 Element Plus tag 类型（详情面板用）
const getScheduleDetailTagType = (shiftType: string): 'primary' | 'success' | 'warning' | 'danger' | 'info' => {
	switch (shiftType) {
		case '排班':
			return 'success';
		case '正常上班':
			return 'primary';
		case '请假':
			return 'warning';
		case '调休':
			return 'info';
		case '替班':
			return 'warning';
		case '换班':
			return 'primary';
		case '加班':
			return 'danger';
		default:
			return 'info';
	}
};

// 获取考勤结果对应的 Element Plus tag 类型
const getAttendanceResultTagType = (result: string): 'primary' | 'success' | 'warning' | 'danger' | 'info' => {
	if (!result) return 'info';

	// 根据后端 AttendanceResultEnum 枚举值进行匹配
	switch (result) {
		case '未计算':
			return 'info';
		case '正常':
			return 'success';
		case '迟到':
			return 'warning';
		case '早退':
			return 'warning';
		case '迟到且早退':
			return 'danger';
		case '缺卡':
			return 'danger';
		case '旷工':
			return 'danger';
		default:
			// 兼容其他可能的值，使用原来的模糊匹配逻辑
			const lowerResult = result.toLowerCase();
			if (lowerResult.includes('正常') || lowerResult.includes('出勤')) {
				return 'success';
			} else if (lowerResult.includes('迟到') || lowerResult.includes('早退')) {
				return 'warning';
			} else if (lowerResult.includes('缺勤') || lowerResult.includes('旷工') || lowerResult.includes('缺卡')) {
				return 'danger';
			} else if (lowerResult.includes('请假') || lowerResult.includes('休假')) {
				return 'info';
			} else {
				return 'primary';
			}
	}
};

// 在窗口尺寸变化时，尝试重新定位卡片，避免溢出
const onWindowResize = () => {
	if (!showScheduleDetail.value) return;
	nextTick(() => {
		const cardEl = scheduleDetailCardRef.value?.$el || scheduleDetailCardRef.value;
		if (!cardEl) return;
		const cardRect = cardEl.getBoundingClientRect();
		const vw = window.innerWidth;
		const vh = window.innerHeight;
		const margin = 12;

		let x = cardPosition.value.x;
		let y = cardPosition.value.y;

		const halfWidth = cardRect.width / 2;
		x = Math.max(margin + halfWidth, Math.min(vw - margin - halfWidth, x));

		if (cardTransform.value.includes('-100%')) {
			y = Math.max(margin + cardRect.height, y);
		} else {
			y = Math.min(vh - margin - cardRect.height, y);
		}

		cardPosition.value = { x, y };
	});
};

const getCellClasses = (person: any, date: string) => {
	const schedule = getScheduleForPerson(person, date);
	const dayInfo = visibleDays.value.find((day) => day.date === date);

	const classes = [];

	if (dayInfo?.isWeekend) {
		classes.push('weekend');
	}

	if (!schedule) {
		classes.push('empty-schedule');
	} else {
		// 优先检查是否是休班
		if (isRestDay(schedule)) {
			// 休班也使用接口返回的颜色，如果没有则使用默认休班样式
			if (schedule.DisplayColor) {
				classes.push('has-schedule');
			} else {
				classes.push('rest-shift');
			}
		} else {
			// 所有非休班的排班都优先使用接口返回的颜色
			if (schedule.DisplayColor) {
				classes.push('has-schedule');
			} else {
				// 如果没有接口颜色，使用通用的排班样式，不再根据班次类型区分
				classes.push('has-schedule-default');
			}
		}

		// 异常状态
		if (schedule.isAbnormal) {
			classes.push('abnormal');
		}
	}

	// 批量模式样式
	if (batchMode.value) {
		classes.push('batch-mode-cell');

		// 选中状态
		if (isCellSelected(person, date)) {
			classes.push('selected-cell');

			// 判断是否是范围的起点或终点
			if (isRangeStart(person, date)) {
				classes.push('range-start');
			} else if (isRangeEnd(person, date)) {
				classes.push('range-end');
			} else if (selectedCells.value.size > 1) {
				classes.push('in-selection-range');
			}
		}

		// 拖拽选择预览
		if (isDragging.value && isInDragPreview(person, date)) {
			classes.push('drag-selecting');
		}
	}

	return classes;
};

// 获取单元格动态样式
const getCellStyle = (person: any, date: string) => {
	const schedule = getScheduleForPerson(person, date);

	if (!schedule || !schedule.DisplayColor) {
		return {};
	}

	return {
		backgroundColor: schedule.DisplayColor,
		borderRadius: '6px',
		margin: '1px',
	};
};

const previousMonth = () => {
	currentDate.value = new Date(currentDate.value.getFullYear(), currentDate.value.getMonth() - 1, 1);
	// 触发日期变更事件，通知父组件重新获取数据
	emit('date-change', {
		year: currentDate.value.getFullYear(),
		month: currentDate.value.getMonth() + 1,
	});
};

const nextMonth = () => {
	currentDate.value = new Date(currentDate.value.getFullYear(), currentDate.value.getMonth() + 1, 1);
	// 触发日期变更事件，通知父组件重新获取数据
	emit('date-change', {
		year: currentDate.value.getFullYear(),
		month: currentDate.value.getMonth() + 1,
	});
};

const handleCellClick = (person: any, date: string) => {
	if (batchMode.value) {
		// 批量模式下使用拖拽选择，不处理点击事件
		return;
	} else {
		// 正常模式下显示详情
		const schedule = getScheduleForPerson(person, date);
		emit('cell-click', {
			person,
			date,
			schedule,
		});
	}
};

// ==================== 日期列头快速选择功能 ====================

/**
 * 单击日期列头 - 高亮该列
 */
const handleDateColumnClick = (date: string) => {
	if (!batchMode.value) return;

	// 单击时高亮列
	if (selectedDateColumn.value === date) {
		selectedDateColumn.value = null; // 再次点击取消选择
	} else {
		selectedDateColumn.value = date;
	}
};

/**
 * 双击日期列头 - 快速选择该日期的所有人员
 */
const handleDateColumnDblClick = (date: string) => {
	// 只有在批量模式下才能双击日期列头快速排班
	if (!batchMode.value) {
		ElMessage.warning('请先点击"开始排班"按钮进入批量模式，然后再双击日期进行批量排班');
		return;
	}

	// 检查是否有选择人员
	if (selectedPersonsForBatch.value.size === 0) {
		ElMessage.warning('请先勾选左侧人员复选框');
		return;
	}

	// 显示选择范围对话框
	pendingSelectDate.value = date;
	dateColumnSelectScope.value = 'current-page'; // 默认选择当前页
	showDateColumnSelectDialog.value = true;
};

/**
 * 确认日期列选择
 */
const confirmDateColumnSelection = async () => {
	const targetDate = pendingSelectDate.value;
	if (!targetDate) return;

	// 清除之前的选择
	selectedCells.value.clear();
	selectedDateRange.value = null;

	// 只为已选择的人员添加单元格
	let selectedCount = 0;
	selectedPersonsForBatch.value.forEach((personId) => {
		const cellKey = `${personId}_${targetDate}`;
		selectedCells.value.add(cellKey);
		selectedCount++;
	});

	ElMessage.success(`已选择 ${selectedCount} 人的 ${formatDateForDisplay(targetDate)} 排班`);

	// 设置选择范围
	selectedDateRange.value = { start: targetDate, end: targetDate };
	selectedDateColumn.value = targetDate;

	// 关闭对话框
	showDateColumnSelectDialog.value = false;

	// 如果不在批量模式，自动进入批量模式
	if (!batchMode.value) {
		batchMode.value = true;
	}

	// 自动打开批量排班面板
	nextTick(() => {
		showBatchPanel.value = true;
		// 重新计算弹窗位置确保居中
		batchPanelPosition.value = calculateCenterPosition();
	});
};

/**
 * 获取人员数据（优先从完整加载的数据中查找）
 */
const getPersonData = (personId: string) => {
	// 优先从完整加载的数据中查找
	if (fullLoadedPersons.value.length > 0) {
		return fullLoadedPersons.value.find((p: any) => p.personId === personId);
	}
	// 如果没有完整数据，从当前页数据中查找
	return scheduleData.value.find((p: any) => p.personId === personId);
};

/**
 * 选择全部分页的指定日期
 */
const selectAllPagesForDate = async (date: string) => {
	if (!props.selectedDeptId) {
		ElMessage.warning('请先选择部门');
		return;
	}

	const loading = ElLoading.service({
		lock: true,
		text: '正在加载全部人员数据，请稍候...',
		background: 'rgba(0, 0, 0, 0.7)',
	});

	try {
		const api = attendanceApi();

		// 第一步：获取总人数
		const countRes = await api.getSchedulingByPerson({
			GroupId: props.selectedDeptId,
			Page: 1,
			Limit: 1,
			IsPlainText: true,
			IsEmunToString: true,
		});

		const totalCount = (countRes as any).Total || (countRes as any).TotalCount || (countRes as any).Count || 99999;

		loading.setText(`正在加载全部 ${totalCount} 人的数据，请稍候...`);

		// 第二步：获取所有人员数据
		const res = await api.getSchedulingByPerson({
			GroupId: props.selectedDeptId,
			Page: 1,
			Limit: totalCount,
			IsPlainText: true,
			IsEmunToString: true,
		});

		if (res.Data && Array.isArray(res.Data) && res.Data.length > 0) {
			// 存储完整的人员数据
			fullLoadedPersons.value = res.Data.map((person: any) => ({
				personId: person.ID || person.id || person.PersonId,
				personName: person.Name || person.name || person.PersonName,
				personCode: person.StaffNo || person.staffNo || '',
				records: person.Records || [],
				rawData: person,
			}));

			// 清空并选择所有人员的指定日期
			selectedCells.value.clear();
			res.Data.forEach((person: any) => {
				const personId = person.ID || person.id || person.PersonId;
				if (personId) {
					selectedCells.value.add(`${personId}_${date}`);
				}
			});

			ElMessage.success({
				message: `已选择全部 ${res.Data.length} 人的 ${formatDateForDisplay(date)} 排班`,
				duration: 3000,
			});
		} else {
			// 降级为选择当前页人员
			fullLoadedPersons.value = [];
			scheduleData.value.forEach((person: any) => {
				selectedCells.value.add(`${person.personId}_${date}`);
			});
			ElMessage.warning('获取全部人员数据失败，已选择当前页人员');
		}
	} catch (error) {
		console.error('获取全部人员数据失败:', error);
		// 降级为选择当前页人员
		fullLoadedPersons.value = [];
		scheduleData.value.forEach((person: any) => {
			selectedCells.value.add(`${person.personId}_${date}`);
		});
		ElMessage.error('获取全部人员数据失败，已选择当前页人员');
	} finally {
		loading.close();
	}
};

/**
 * 获取日期列的选中数量
 */
const getDateColumnSelectionCount = (date: string): number => {
	if (!selectedCells.value.size) return 0;

	let count = 0;
	selectedCells.value.forEach((cellKey) => {
		if (cellKey.endsWith(`_${date}`)) {
			count++;
		}
	});
	return count;
};

/**
 * 格式化日期用于显示
 */
const formatDateForDisplay = (dateStr: string): string => {
	if (!dateStr) return '';

	try {
		const date = new Date(dateStr);
		const year = date.getFullYear();
		const month = (date.getMonth() + 1).toString().padStart(2, '0');
		const day = date.getDate().toString().padStart(2, '0');
		const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
		const weekday = weekdays[date.getDay()];

		return `${year}年${month}月${day}日 ${weekday}`;
	} catch (error) {
		return dateStr;
	}
};

// ==================== 日期列头快速选择功能 END ====================

// 批量模式下的单元格鼠标事件处理
let isDragging = ref(false);

const handleBatchCellMouseDown = (person: any, date: string, event: MouseEvent) => {
	if (!batchMode.value) return;

	// 如果没有选择人员，提示用户先选择人员
	if (selectedPersonsForBatch.value.size === 0) {
		ElMessage.warning('请先勾选左侧人员复选框');
		return;
	}

	event.preventDefault();
	isDragging.value = true;
	rangeStartDate.value = date;
	rangeStartPerson.value = person;
	selectedCells.value.clear();
	selectedDateRange.value = { start: date, end: date };
	isRangeSelecting.value = true;

	// 为所有已选择的人员在当前日期添加单元格
	selectedPersonsForBatch.value.forEach((personId: string) => {
		const cellKey = `${personId}_${date}`;
		selectedCells.value.add(cellKey);
	});
};

const handleBatchCellMouseEnter = (person: any, date: string) => {
	if (!batchMode.value || !isDragging.value || !rangeStartDate.value || !rangeStartPerson.value) return;

	// 计算矩形区域的边界
	const startDate = new Date(rangeStartDate.value);
	const currentDate = new Date(date);

	// 确定日期范围
	const minDate = startDate <= currentDate ? rangeStartDate.value : date;
	const maxDate = startDate <= currentDate ? date : rangeStartDate.value;
	selectedDateRange.value = { start: minDate, end: maxDate };

	selectedCells.value.clear();

	// 生成日期列表
	const dateList: string[] = [];
	const tempDate = new Date(minDate);
	const endDate = new Date(maxDate);

	while (tempDate <= endDate) {
		dateList.push(formatDateToYYYYMMDD(tempDate));
		tempDate.setDate(tempDate.getDate() + 1);
	}

	// 只为已选择的人员添加单元格（不再基于拖拽的人员范围）
	selectedPersonsForBatch.value.forEach((personId: string) => {
		for (const cellDate of dateList) {
			const cellKey = `${personId}_${cellDate}`;
			selectedCells.value.add(cellKey);
		}
	});
};

const handleBatchCellMouseUp = (person: any, date: string) => {
	if (!batchMode.value || !isDragging.value) return;

	isDragging.value = false;
	isRangeSelecting.value = false;

	// 如果有选中的单元格，显示批量面板
	if (selectedCells.value.size > 0) {
		// 计算精确的居中位置
		batchPanelPosition.value = calculateCenterPosition();
		batchPanelDragging.value = false;

		// 根据选中单元格的状态设置默认班次类型
		if (hasAbnormalCells.value) {
			// 如果选中的是异常单元格，默认选择"正常上班"
			batchForm.value.ShiftType = '正常上班';
		} else if (hasNoScheduleCells.value) {
			// 如果选中的是无排班单元格，默认选择"排班"
			batchForm.value.ShiftType = '排班';
		} else {
			// 其他情况，默认选择"排班"
			batchForm.value.ShiftType = '排班';
		}

		showBatchPanel.value = true;

		// 计算选中的员工数量和日期范围
		const selectedPersonIds = new Set();
		selectedCells.value.forEach((cellKey) => {
			const [personId] = cellKey.split('_');
			selectedPersonIds.add(personId);
		});
	}
};

// 全局鼠标抬起事件，处理拖拽结束
const handleGlobalMouseUp = () => {
	if (isDragging.value) {
		isDragging.value = false;
		isRangeSelecting.value = false;
	}
};

const handleCellDblClick = (person: any, date: string) => {
	if (!person || !date) return;

	try {
		emit('cell-dblclick', {
			person,
			date,
			schedule: getScheduleForPerson(person, date),
		});
	} catch (error) {
		// Double click cell event handling error
	}
};

// El-Table布局初始化
const initTableLayout = () => {
	// 确保El-Table组件正确渲染
	if (scheduleTableRef.value) {
		// 调用El-Table的重新布局方法
		scheduleTableRef.value.doLayout?.();
		// El-Table layout initialization completed
	}
};

// 获取员工姓名首字母
const getEmployeeInitials = (name: string) => {
	if (!name) return '';
	const chars = name.split('');
	if (chars.length === 1) return chars[0];
	return chars.slice(-2).join(''); // 取最后两个字符（中文姓名）
};

// ==================== El-Table 相关方法 ====================

// 计算表格高度
const tableHeight = computed(() => {
	// 动态计算表格高度，为分页组件留出空间
	return 'calc(100vh - 250px)';
});

// 分页事件处理
const handleSizeChange = (newSize: number) => {
	try {
		pageSize.value = newSize;
		currentPage.value = 1; // 重置到第一页

		// 清除批量选择状态
		if (batchMode.value) {
			clearBatchSelection();
		}

		// 发送分页变化事件给父组件（用于后端分页）
		emit('page-change', {
			page: currentPage.value,
			limit: pageSize.value,
		});

		// 滚动到顶部
		nextTick(() => {
			if (scheduleTableContainer.value) {
				scheduleTableContainer.value.scrollTop = 0;
			}
		});
	} catch (error) {
		console.error('Error handling page size change:', error);
	}
};

const handleCurrentChange = (newPage: number) => {
	try {
		currentPage.value = newPage;

		// 清除批量选择状态
		if (batchMode.value) {
			clearBatchSelection();
		}

		// 发送分页变化事件给父组件（用于后端分页）
		emit('page-change', {
			page: currentPage.value,
			limit: pageSize.value,
		});

		// 滚动到顶部
		nextTick(() => {
			if (scheduleTableContainer.value) {
				scheduleTableContainer.value.scrollTop = 0;
			}
		});
	} catch (error) {
		console.error('Error handling page change:', error);
	}
};

// El-Table行类名
const getRowClassName = ({ row, rowIndex }: { row: any; rowIndex: number }) => {
	const classes = ['schedule-row'];
	if (isEmployeeSelected(row.personId)) {
		classes.push('is-selected');
	}
	return classes.join(' ');
};

// El-Table单元格类名
const getCellClassName = ({ row, column, rowIndex, columnIndex }: any) => {
	// 员工信息列的特殊处理
	if (columnIndex === 0) {
		return 'employee-cell';
	}

	// 日期列的处理
	const dayInfo = getDayInfoFromColumn(column, columnIndex);
	if (!dayInfo) return '';

	const classes = ['schedule-cell-wrapper'];

	if (dayInfo.isWeekend) classes.push('is-weekend');
	if (dayInfo.isCurrentMonth) classes.push('is-current-month');

	return classes.join(' ');
};

// 日期列类名
const getDateColumnClassName = (day: any) => {
	const classes = ['date-column'];
	if (day.isWeekend) classes.push('is-weekend');
	if (day.isCurrentMonth) classes.push('is-current-month');
	if (isToday(day.date)) classes.push('is-today');
	return classes.join(' ');
};

// 从列信息获取日期信息
const getDayInfoFromColumn = (column: any, columnIndex: number) => {
	if (columnIndex === 0) return null; // 员工信息列

	const dayIndex = columnIndex - 1;
	return visibleDays.value[dayIndex] || null;
};

// 处理行点击事件
const handleRowClick = (row: any) => {
	// 可以在这里处理行点击逻辑
	// Row clicked
};

// 检查是否为今天
const isToday = (dateStr: string) => {
	if (!dateStr) return false;

	try {
		const today = new Date();
		// 使用统一的日期格式化函数
		const todayStr = formatDateToYYYYMMDD(today);

		// 直接比较字符串，避免时区问题
		return dateStr === todayStr;
	} catch (error) {
		// Error in isToday
		return false;
	}
};

// 判断日期是否是过去（已上班）
const isPastDate = (dateStr: string) => {
	if (!dateStr) return false;

	try {
		const today = new Date();
		const todayStr = formatDateToYYYYMMDD(today);

		// 日期字符串比较，小于今天的日期
		return dateStr < todayStr;
	} catch (error) {
		return false;
	}
};

// 判断班次是否已计算或已上班（需要显示为灰色）
const isCompletedOrPast = (person: any, date: string) => {
	const schedule = getScheduleForPerson(person, date);
	if (!schedule) return false;

	// 判断是否是过去的日期
	const isPast = isPastDate(date);

	// 判断是否已计算（attendanceResult 不为空且不为"未计算"）
	const result = schedule?.attendanceResult || '';
	const isCalculated = result && result !== '未计算';

	// 需要同时满足：上过班（过去的日期）且有考勤结果才置灰
	return isPast && isCalculated;
};

// 检查员工是否被选中
const isEmployeeSelected = (personId: string) => {
	if (!personId) return false;
	// 简化：不再需要复杂的员工选择逻辑
	return false;
};

// 获取员工月度统计
const getMonthlyStats = (person: any) => {
	let abnormalDays = 0; // 实际异常天数（用于内部计算）
	let workDays = 0; // 排班天数
	let restDays = 0; // 休息天数

	// 遍历当前月份的所有天数，统计排班天数和异常天数
	visibleDays.value.forEach((day) => {
		const schedule = getScheduleForPerson(person, day.date);

		if (schedule) {
			// 有排班就计入工作天数
			workDays++;

			// 根据考勤结果判断是否异常
			const attendanceResult = schedule.attendanceResult;

			// 考勤结果异常的情况：迟到、早退、迟到且早退、缺卡、旷工（排除"未计算"）
			const isAttendanceAbnormal = attendanceResult && attendanceResult !== '正常' && attendanceResult !== '未计算';

			// 或者有异常标识
			const hasAbnormalFlag = schedule.isAbnormal;

			if (isAttendanceAbnormal || hasAbnormalFlag) {
				abnormalDays++;
			}
		} else {
			// 如果是工作日但无排班，也视为异常
			if (!day.isWeekend) {
				abnormalDays++;
			}
		}
	});

	restDays = visibleDays.value.length - workDays;

	// 计算本月工作日天数（非周末天数）
	const workingDaysInMonth = visibleDays.value.filter((day) => !day.isWeekend).length;

	return {
		workDays, // 排班天数
		restDays, // 休息天数
		abnormalDays: abnormalDays > 0 ? abnormalDays : workingDaysInMonth, // 有异常显示异常天数，无异常显示工作日天数
		actualAbnormalDays: abnormalDays, // 实际异常天数，用于其他业务逻辑
	};
};
/**
 * 刷新排班状态
 */
// 刷新排班数据
const refreshSchedule = async () => {
	if (refreshLoading.value) return; // 防止重复点击

	try {
		refreshLoading.value = true;

		// 触发父组件重新加载数据
		emit('refresh-data');

		// 等待一小段时间让父组件有时间处理
		await new Promise((resolve) => setTimeout(resolve, 800));

		ElMessage.success('排班数据已刷新');
	} catch (error) {
		// Failed to refresh schedule data
		ElMessage.error('刷新失败，请重试');
	} finally {
		refreshLoading.value = false;
	}
};

/**
 * 处理员工点击事件
 * @param person 员工数据
 */
const handleEmployeeClick = (person: any) => {
	if (!person) return;

	try {
		if (batchMode.value) {
			// 批量模式下点击员工卡片，切换选择状态
			const isSelected = selectedPersonsForBatch.value.has(person.personId);
			if (isSelected) {
				selectedPersonsForBatch.value.delete(person.personId);
			} else {
				selectedPersonsForBatch.value.add(person.personId);
			}
			updateSelectAllState();
		} else {
			// Person data
			// 正常模式下查看个人排班
			const personInfo = {
				personId: person.personId,
			};
			selectedPersonData.value = personInfo;
			showPersonalSchedule.value = true;
		}
	} catch (error) {
		// Error handling employee click event
		ElMessage.error('操作失败，请重试');
	}
};

/**
 * 处理单个人员复选框变化
 * @param person 员工数据
 * @param checked 是否选中
 */
const handlePersonCheckChange = (person: any, checked: boolean) => {
	if (checked) {
		selectedPersonsForBatch.value.add(person.personId);
	} else {
		selectedPersonsForBatch.value.delete(person.personId);
	}
	updateSelectAllState();
};

/**
 * 处理全选/取消全选
 * @param checked 是否选中
 */
const handleSelectAllChange = (checked: boolean) => {
	if (checked) {
		// 全选：将当前页所有人员添加到选中集合
		scheduleData.value.forEach((person: any) => {
			selectedPersonsForBatch.value.add(person.personId);
		});
	} else {
		// 取消全选：清空选中集合
		selectedPersonsForBatch.value.clear();
	}
	updateSelectAllState();
};

/**
 * 更新全选状态
 */
const updateSelectAllState = () => {
	const total = scheduleData.value.length;
	const selected = scheduleData.value.filter((person: any) => selectedPersonsForBatch.value.has(person.personId)).length;

	if (selected === 0) {
		selectAllPersons.value = false;
		isIndeterminate.value = false;
	} else if (selected === total) {
		selectAllPersons.value = true;
		isIndeterminate.value = false;
	} else {
		selectAllPersons.value = false;
		isIndeterminate.value = true;
	}
};

/**
 * 处理个人排班弹窗关闭
 */
const handlePersonalScheduleClose = () => {
	try {
		showPersonalSchedule.value = false;
		selectedPersonData.value = null;
	} catch (error) {
		// Error closing personal schedule popup
	}
};

/**
 * 处理个人排班数据更新
 */
const handlePersonalScheduleUpdated = async (data: { type: 'single' | 'batch'; success: boolean }) => {
	try {
		if (data.success) {
			// 个人排班更新成功，自动刷新团队日历数据
			await refreshSchedule();
		}
	} catch (error) {
		// 刷新团队日历数据失败，静默处理
	}
};

// ==================== 批量排班功能 ====================

// 切换批量模式
const toggleBatchMode = () => {
	batchMode.value = !batchMode.value;

	if (batchMode.value) {
		// 重置选择状态
		selectedPersons.value.clear();
		selectedDateRange.value = null;
		isRangeSelecting.value = false;
		isDragging.value = false;
		showBatchPanel.value = false;
		selectedPersonsForBatch.value.clear(); // 清空复选框选择
		selectAllPersons.value = false;
		isIndeterminate.value = false;
	} else {
		// 退出批量模式
		clearBatchSelection();
		selectedPersonsForBatch.value.clear(); // 清空复选框选择
		selectAllPersons.value = false;
		isIndeterminate.value = false;
	}
};

// 获取选择范围文本
const getSelectionRangeText = () => {
	if (!selectedDateRange.value) return '';

	const startDate = parseDateString(selectedDateRange.value.start);
	const endDate = parseDateString(selectedDateRange.value.end);

	if (!startDate || !endDate) return '';

	const formatDisplayDate = (date: Date) => {
		return `${date.getMonth() + 1}/${date.getDate()}`;
	};

	if (selectedDateRange.value.start === selectedDateRange.value.end) {
		return formatDisplayDate(startDate);
	}

	return `${formatDisplayDate(startDate)} - ${formatDisplayDate(endDate)}`;
};

// 处理换班人员选择
const handleSwapPersonChange = async (personId: string | null, personData: any) => {
	if (personId && personData) {
		// 清空之前的换班选项
		batchForm.value.SwapShiftId = '';
		swapShiftOptions.value = [];

		// 查询该人员的排班记录
		await loadPersonScheduleRecords(personId);
	} else {
		// 清空换班相关数据
		batchForm.value.SwapShiftId = '';
		swapShiftOptions.value = [];
	}
};

// 查询人员排班记录
const loadPersonScheduleRecords = async (personId: string) => {
	try {
		swapShiftLoading.value = true;

		// 构建查询参数
		const params: ISearchByPersonRequest = {
			PersonId: personId,
			Page: 1,
			Limit: 50,
			IsPlainText: true,
			IsEmunToString: true,
			// 查询当天开始后30天的排班记录
			ShiftDate: [formatDate(new Date(), 'yyyy-MM-dd'), formatDate(new Date(new Date().getTime() + 30 * 24 * 60 * 60 * 1000), 'yyyy-MM-dd')],
		};

		const response = await attendanceApi().getSchedulingByPerson(params);

		if (response.Data && Array.isArray(response.Data) && response.Data.length > 0) {
			// 转换为选择器选项格式：日期-班次
			const firstData = (response.Data as any[])[0];
			if (firstData && firstData.Records && Array.isArray(firstData.Records)) {
				const filteredData = firstData.Records.filter((record: any) => {
					// 确保记录有必要的字段
					return record.ID && record.ShiftDate && record.AttendanceShiftGroupName;
				});

				swapShiftOptions.value = filteredData.map((record: any) => {
					// 优先使用完整班组信息，不再使用 ShiftInfo
					const shiftName = record.AttendanceShiftGroupName || record.AttendanceShift?.Name || record.ShiftName || '未知班次';
					// const shiftName = record.AttendanceShift?.Name || record.AttendanceShiftGroupName || record.ShiftName || record.ShiftInfo || '未知班次'; // 注释掉 ShiftInfo
					return {
						value: record.ID,
						label: `${record.ShiftDate} - ${shiftName}`,
					};
				});
			} else {
				swapShiftOptions.value = [];
			}
		} else {
			swapShiftOptions.value = [];
		}

		if (swapShiftOptions.value.length === 0) {
			ElMessage.info('该人员暂无可用的排班记录');
		}
	} catch (error) {
		console.error('查询人员排班记录失败:', error);
		ElMessage.error('查询人员排班记录失败');
		swapShiftOptions.value = [];
	} finally {
		swapShiftLoading.value = false;
	}
};

// 退出批量模式
const exitBatchMode = () => {
	batchMode.value = false;
	clearBatchSelection();
};

// 清除批量选择
const clearBatchSelection = () => {
	selectedPersons.value.clear();
	selectedCells.value.clear();
	selectedDateRange.value = null;
	isRangeSelecting.value = false;
	isDragging.value = false;
	rangeStartDate.value = '';
	rangeStartPerson.value = null;
	showBatchPanel.value = false;
	selectedAttendanceGroup.value = '';
	// 清除批量排班表单数据
	batchForm.value = {
		ShiftType: '排班', // 默认选择排班
		SwapShiftId: '',
		SwapPersonId: '',
		SubstitutePersonId: '',
		DateRange: null,
		AutoCalculateFuture: false,
		Remark: '',
	};

	// 清空换班相关数据
	swapShiftOptions.value = [];
};

// 处理员工选择（已移除，现在通过拖拽直接选择区域）

// 批量排班API调用
const batchChangeSchedule = async () => {
	if (selectedCells.value.size === 0) {
		ElMessage.error('请先选择需要排班的时间段');
		return;
	}

	// 验证选择：如果包含无排班状态，只能选择排班
	if (hasNoScheduleCells.value && batchForm.value.ShiftType !== '排班') {
		ElMessage.warning('选中区域包含无排班状态，只能设置为排班');
		return;
	}

	// 验证考勤组必须选择（仅排班需要手动选择）
	if (batchForm.value.ShiftType === '排班' && !selectedAttendanceGroup.value) {
		ElMessage.error('请选择考勤组');
		return;
	}

	// 验证不同班次类型的必填字段
	if (batchForm.value.ShiftType === '替班' && !batchForm.value.SubstitutePersonId) {
		ElMessage.error('请选择替班人员');
		return;
	}

	if (batchForm.value.ShiftType === '换班' && (!batchForm.value.SwapShiftId || !batchForm.value.SwapPersonId)) {
		ElMessage.error('请完善换班信息');
		return;
	}

	// 除了"排班"之外的所有班次类型都需要备注
	if (batchForm.value.ShiftType !== '排班' && !batchForm.value.Remark?.trim()) {
		ElMessage.error('请填写备注原因');
		return;
	}

	batchLoading.value = true;

	try {
		// 动态导入API
		const api = attendanceApi();

		// 收集所有人员ID和日期
		const allPersonIds = new Set<string>();
		const allDates: string[] = [];

		selectedCells.value.forEach((cellKey) => {
			const [personId, date] = cellKey.split('_');
			allPersonIds.add(personId);
			if (!allDates.includes(date)) {
				allDates.push(date);
			}
		});
		// 按日期排序，确保日期范围是有序的
		allDates.sort();
		// 获取开始和结束日期
		const startDate = allDates[0]; // 开始日期
		const endDate = allDates[allDates.length - 1]; // 结束日期
		const dateRange = [startDate, endDate]; // 只包含开始和结束日期

		// 构建请求参数，根据班次类型决定传递哪些字段
		const batchRequest: any = {
			PersonIds: Array.from(allPersonIds), // 所有选中的人员ID
			ShiftType: batchForm.value.ShiftType, //班次类型
			DateRange: dateRange, //日期范围：[开始日期, 结束日期]
			AutoCalculateFuture: batchForm.value.AutoCalculateFuture, //自动计算未来日期
		};

		// 设置考勤组ID
		if (batchForm.value.ShiftType === '排班') {
			// 排班使用用户选择的考勤组
			batchRequest.AttendanceShiftGroupId = selectedAttendanceGroup.value;
		} else {
			// 其他班次类型从选中单元格中获取考勤组ID
			// 取第一个选中单元格的考勤组ID作为默认值
			const firstSelectedCell = Array.from(selectedCells.value)[0];
			if (firstSelectedCell) {
				const [personId, date] = firstSelectedCell.split('_');
				// 使用辅助函数查找人员（优先从完整数据中查找）
				const person = getPersonData(personId);
				if (person) {
					const schedule = getScheduleForPerson(person, date);
					if (schedule && schedule.AttendanceShiftGroupId) {
						batchRequest.AttendanceShiftGroupId = schedule.AttendanceShiftGroupId;
					}
				}
			}
		}
		// 根据班次类型添加相应的额外参数
		switch (batchForm.value.ShiftType) {
			case '替班':
				// 替班需要替班人员
				batchRequest.SubstitutePersonId = batchForm.value.SubstitutePersonId;
				break;
			case '换班':
				// 换班需要换班人员和换班记录
				batchRequest.SwapShiftId = batchForm.value.SwapShiftId;
				batchRequest.SwapPersonId = batchForm.value.SwapPersonId;
				break;
		}

		// 除了"排班"之外的所有班次类型都需要传递备注
		if (batchForm.value.ShiftType !== '排班' && batchForm.value.Remark?.trim()) {
			batchRequest.Remark = batchForm.value.Remark;
		}

		await api.batchChange(batchRequest);

		// 批量排班设置成功
		ElMessage({
			message: `已为 ${allPersonIds.size} 名员工设置 ${selectedCells.value.size} 个时间段的排班`,
			type: 'success',
			duration: 3000,
		});

		// 立即更新本地数据，避免页面刷新
		if (batchForm.value.ShiftType === '排班' || batchForm.value.ShiftType === '替班') {
			updateLocalScheduleData(Array.from(allPersonIds), allDates, selectedAttendanceGroup.value);
		}

		// 立即触发数据刷新，获取服务器最新数据
		emit('schedule-updated', { type: 'batch', success: true, silent: false });

		// 关闭批量面板
		showBatchPanel.value = false;
		clearBatchSelection();
		batchMode.value = false;
	} catch (error) {
		// Batch scheduling request failed
		ElMessage.error('网络错误，请检查网络连接后重试');
	} finally {
		batchLoading.value = false;
	}
};

// 取消批量操作 (点击外部区域时调用)
const cancelBatchOperation = () => {
	// 如果正在拖拽，不执行关闭操作
	if (batchPanelDragging.value) {
		return;
	}

	closeBatchPanel();
};

// 强制关闭批量排班面板 (按钮点击时调用)
const closeBatchPanel = () => {
	showBatchPanel.value = false;
	clearBatchSelection();
	// 重置拖拽位置
	batchPanelPosition.value = { x: 0, y: 0 };
	batchPanelDragging.value = false;
};

// 计算弹窗居中位置
const calculateCenterPosition = () => {
	const viewportWidth = window.innerWidth;
	const viewportHeight = window.innerHeight;

	// 弹窗的预估尺寸 (考虑最小宽度和内容)
	const panelWidth = 480; // min-width
	const panelHeight = 580; // 更精确的预估高度

	// 计算居中位置
	const centerX = (viewportWidth - panelWidth) / 2;
	const centerY = (viewportHeight - panelHeight) / 2;

	// 确保不会超出屏幕边界，留出更多边距
	const margin = 40;
	const x = Math.max(margin, Math.min(centerX, viewportWidth - panelWidth - margin));
	const y = Math.max(margin, Math.min(centerY, viewportHeight - panelHeight - margin));

	return { x, y };
};

// 批量排班面板拖拽相关函数
const onBatchPanelMouseDown = (event: MouseEvent) => {
	batchPanelDragging.value = true;
	batchPanelDragStart.value = {
		x: event.clientX - batchPanelPosition.value.x,
		y: event.clientY - batchPanelPosition.value.y,
	};

	// 添加全局鼠标移动和释放事件
	document.addEventListener('mousemove', onBatchPanelMouseMove);
	document.addEventListener('mouseup', onBatchPanelMouseUp);

	// 防止选中文本
	event.preventDefault();
};

const onBatchPanelMouseMove = (event: MouseEvent) => {
	if (!batchPanelDragging.value) return;

	// 计算新位置
	let newX = event.clientX - batchPanelDragStart.value.x;
	let newY = event.clientY - batchPanelDragStart.value.y;

	// 获取窗口尺寸和面板尺寸，确保不超出边界
	const windowWidth = window.innerWidth;
	const windowHeight = window.innerHeight;
	const panelWidth = 480; // 面板最小宽度
	const panelHeight = 400; // 预估面板高度

	// 边界限制
	newX = Math.max(-panelWidth + 100, Math.min(newX, windowWidth - 100));
	newY = Math.max(-panelHeight + 100, Math.min(newY, windowHeight - 100));

	batchPanelPosition.value = { x: newX, y: newY };
};

const onBatchPanelMouseUp = () => {
	batchPanelDragging.value = false;

	// 移除全局事件监听
	document.removeEventListener('mousemove', onBatchPanelMouseMove);
	document.removeEventListener('mouseup', onBatchPanelMouseUp);
};

// 本地更新排班数据，避免页面刷新
const updateLocalScheduleData = async (personIds: string[], dates: string[], attendanceGroupId: string) => {
	try {
		// 获取考勤组信息
		const selectedGroup = attendanceGroups.value.find((group) => group.id === attendanceGroupId);
		if (!selectedGroup) return;

		// 更新本地数据
		personIds.forEach((personId) => {
			// 在当前数据中找到对应的员工
			const personData = props.data.find((person: any) => person.ID === personId || person.PersonId === personId || person.personId === personId);

			if (personData && personData.Records) {
				dates.forEach((dateStr) => {
					// 查找对应日期的记录
					const existingRecord = personData.Records.find(
						(record: any) => record.ShiftDate === dateStr || formatDateToYYYYMMDD(new Date(record.ShiftDate || '')) === dateStr
					);

					if (existingRecord) {
						// 更新现有记录
						existingRecord.AttendanceShiftGroupId = attendanceGroupId;
						existingRecord.AttendanceShiftGroupName = selectedGroup.name;
						existingRecord.ShiftName = selectedGroup.name;
						existingRecord.ScheduleType = 'assigned';
						existingRecord.DisplayColor = '#409EFF'; // 蓝色表示已排班
					} else {
						// 添加新记录
						personData.Records.push({
							AttendanceShiftGroupId: attendanceGroupId,
							AttendanceShiftGroupName: selectedGroup.name,
							PersonName: (personData as any).Name || (personData as any).PersonName,
							ShiftDate: dateStr,
							ShiftName: selectedGroup.name,
							ScheduleType: 'assigned',
							DisplayColor: '#409EFF',
						});
					}
				});
			}
		});

		// 本地排班数据已更新，避免页面刷新
	} catch (error) {
		// 更新本地排班数据失败
	}
};

// 判断特定单元格是否被选中
const isCellSelected = (person: any, date: string): boolean => {
	if (!batchMode.value) return false;
	const cellKey = `${person.personId}_${date}`;
	return selectedCells.value.has(cellKey);
};

// 判断是否是选择范围的起点
const isRangeStart = (person: any, date: string): boolean => {
	if (!selectedDateRange.value || selectedCells.value.size === 0) return false;
	const cellKey = `${person.personId}_${date}`;
	if (!selectedCells.value.has(cellKey)) return false;

	// 找到所有选中单元格中最早的日期和最小的人员索引
	let earliestDate = date;
	let earliestPersonIndex = scheduleData.value.findIndex((p: any) => p.personId === person.personId);

	selectedCells.value.forEach((key) => {
		const [pid, d] = key.split('_');
		const personIndex = scheduleData.value.findIndex((p: any) => p.personId === pid);
		if (d < earliestDate || (d === earliestDate && personIndex < earliestPersonIndex)) {
			earliestDate = d;
			earliestPersonIndex = personIndex;
		}
	});

	const targetPersonIndex = scheduleData.value.findIndex((p: any) => p.personId === person.personId);
	return targetPersonIndex === earliestPersonIndex && date === earliestDate;
};

// 判断是否是选择范围的终点
const isRangeEnd = (person: any, date: string): boolean => {
	if (!selectedDateRange.value || selectedCells.value.size === 0) return false;
	const cellKey = `${person.personId}_${date}`;
	if (!selectedCells.value.has(cellKey)) return false;

	// 找到所有选中单元格中最晚的日期和最大的人员索引
	let latestDate = date;
	let latestPersonIndex = scheduleData.value.findIndex((p: any) => p.personId === person.personId);

	selectedCells.value.forEach((key) => {
		const [pid, d] = key.split('_');
		const personIndex = scheduleData.value.findIndex((p: any) => p.personId === pid);
		if (d > latestDate || (d === latestDate && personIndex > latestPersonIndex)) {
			latestDate = d;
			latestPersonIndex = personIndex;
		}
	});

	const targetPersonIndex = scheduleData.value.findIndex((p: any) => p.personId === person.personId);
	return targetPersonIndex === latestPersonIndex && date === latestDate;
};

// 判断是否在拖拽预览范围内
const isInDragPreview = (person: any, date: string): boolean => {
	if (!isDragging.value || !rangeStartDate.value || !rangeStartPerson.value) return false;

	// 这里可以添加预览逻辑，暂时返回false，让实际选择的单元格显示样式
	return false;
};

// 加载班组数据
const loadAttendanceGroups = async () => {
	try {
		const api = attendanceGroupApi().search({
			Limit: 100,
			Page: 1,
		});
		// 调用获取班次列表接口
		const response = await api;
		if (response && response.Data) {
			// 将班组数据转换为下拉选项格式
			attendanceGroups.value = response.Data.map((group) => ({
				id: group.ID || '',
				name: group.Name || '',
			}));
		} else {
			ElMessage.warning('未获取到班组数据');
		}
	} catch (error) {
		// Failed to load shift group data
	}
};

// 生命周期
onMounted(() => {
	// 初始化Table布局
	setTimeout(() => {
		initTableLayout();
	}, 100); // 延迟确保DOM完全渲染

	// 加载班组数据
	loadAttendanceGroups();

	// 添加全局鼠标抬起事件监听器，处理拖拽结束
	document.addEventListener('mouseup', handleGlobalMouseUp);

	// 添加窗口大小变化监听器
	window.addEventListener('resize', handleWindowResize);
	window.addEventListener('resize', onWindowResize);
});

onUnmounted(() => {
	// 组件销毁时清理
	document.removeEventListener('mouseup', handleGlobalMouseUp);
	// 清理批量面板拖拽事件监听器
	document.removeEventListener('mousemove', onBatchPanelMouseMove);
	document.removeEventListener('mouseup', onBatchPanelMouseUp);
	// 清理窗口大小变化监听器
	window.removeEventListener('resize', handleWindowResize);
	window.removeEventListener('resize', onWindowResize);
});

// 窗口大小变化处理
const handleWindowResize = () => {
	// 触发响应式计算属性重新计算
	// Vue 3 的响应式系统会自动处理
};

// 监听数据变化，重新初始化Table布局
watch(
	() => props.data,
	() => {
		setTimeout(() => {
			initTableLayout();
		}, 50);
	},
	{ deep: true }
);

// 监听部门ID变化，自动加载该部门人员
watch(
	() => props.selectedDeptId,
	(newDeptId) => {
		if (newDeptId) {
			loadDeptPersons(newDeptId);
		} else {
			deptPersons.value = [];
		}
	},
	{ immediate: true }
);

// 监听数据变化，同步更新个人排班弹窗数据
watch(
	() => processAllScheduleData.value,
	(newAllScheduleData) => {
		// 如果个人排班弹窗正在显示，且有选中的人员数据
		if (showPersonalSchedule.value && selectedPersonData.value) {
			// 在新数据中找到对应的人员
			const updatedPersonData = newAllScheduleData.find((person: any) => person.personId === selectedPersonData.value.personId);

			if (updatedPersonData) {
				// 更新个人排班弹窗的数据（只更新基本信息，排班数据由个人组件自己加载）
				selectedPersonData.value = {
					personId: updatedPersonData.personId,
					personName: updatedPersonData.personName,
					personCode: updatedPersonData.personCode,
					department: (updatedPersonData as any).department || '',
				};
			}
		}
	},
	{ deep: true }
);

// ==================== Element Plus 拖拽排班功能 ====================
</script>

<style scoped lang="scss">
/* ==================== 基础样式 ==================== */
.schedule-calendar {
	width: 100%;
	height: 100%;
	background: var(--calendar-bg-primary, linear-gradient(135deg, #ffffff 0%, #f8fafc 100%));
	border-radius: 20px;
	box-shadow:
		0 1px 3px rgba(0, 0, 0, 0.05),
		0 20px 25px rgba(0, 0, 0, 0.02);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
}

/* ==================== 顶部栏样式 ==================== */
.calendar-header {
	background: var(--calendar-bg-secondary, linear-gradient(135deg, #f8fafc 0%, #ffffff 100%));
	padding: 24px 32px;
	display: flex;
	justify-content: space-between;
	align-items: center;
	color: var(--calendar-text-primary, #1f2937);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
	border-bottom: 1px solid var(--calendar-border-color, #e5e7eb);

	.header-left {
		.month-display {
			display: flex;
			align-items: center;
			gap: 24px;

			.month-info {
				.current-month {
					font-size: 32px;
					font-weight: 800;
					margin: 0;
					line-height: 1;
					color: var(--calendar-text-primary, #111827);
					letter-spacing: -0.025em;
				}

				.today-info {
					margin-top: 8px;
					font-size: 14px;
					color: var(--calendar-text-secondary, #6b7280);
					display: flex;
					align-items: center;
					gap: 8px;

					.today-label {
						font-weight: 500;
					}

					.today-date {
						background: var(--calendar-bg-hover, #f3f4f6);
						color: var(--calendar-text-primary, #374151);
						padding: 4px 12px;
						border-radius: 16px;
						font-weight: 600;
						font-size: 13px;
					}
				}
			}

			.nav-controls {
				display: flex;
				gap: 12px;

				.nav-btn {
					width: 44px;
					height: 44px;
					border: 1px solid var(--calendar-border-color, #e5e7eb);
					border-radius: 12px;
					background: var(--calendar-bg-secondary, #ffffff);
					color: var(--calendar-text-primary, #374151);
					cursor: pointer;
					transition: all 0.2s ease;
					display: flex;
					align-items: center;
					justify-content: center;
					box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);

					&:hover {
						background: var(--calendar-bg-hover, #f9fafb);
						border-color: var(--calendar-border-color, #d1d5db);
						transform: translateY(-1px);
						box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
					}

					&:active {
						transform: translateY(0);
						box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
					}

					.nav-text {
						font-size: 18px;
						font-weight: 600;
					}
				}
			}
		}
	}

	.header-right {
		.action-buttons {
			display: flex;
			gap: 12px;

			.action-btn {
				padding: 12px 20px;
				border: 1px solid var(--calendar-border-color, #e5e7eb);
				border-radius: 12px;
				background: var(--calendar-bg-secondary, #ffffff);
				color: var(--calendar-text-primary, #374151);
				cursor: pointer;
				transition: all 0.2s ease;
				font-weight: 600;
				font-size: 14px;
				box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);

				&:hover:not(:disabled) {
					background: var(--calendar-bg-hover, #f9fafb);
					border-color: var(--calendar-border-color, #d1d5db);
					transform: translateY(-1px);
					box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
				}

				&:active:not(:disabled) {
					transform: translateY(0);
				}

				&:disabled {
					opacity: 0.6;
					cursor: not-allowed;
				}

				&.loading {
					position: relative;
					color: var(--calendar-text-secondary, #9ca3af);

					&::before {
						content: '';
						position: absolute;
						left: 8px;
						top: 50%;
						transform: translateY(-50%);
						width: 16px;
						height: 16px;
						border: 2px solid var(--calendar-border-color, #e5e7eb);
						border-top: 2px solid var(--shift-active-border, #3b82f6);
						border-radius: 50%;
						animation: spin 1s linear infinite;
					}
				}

				@keyframes spin {
					0% {
						transform: translateY(-50%) rotate(0deg);
					}
					100% {
						transform: translateY(-50%) rotate(360deg);
					}
				}

				&.active {
					background: #3b82f6;
					color: white;
					border-color: #3b82f6;

					&:hover {
						background: #2563eb;
						border-color: #2563eb;
					}
				}

				&.clear-btn {
					background: #ef4444;
					color: white;
					border-color: #ef4444;

					&:hover {
						background: #dc2626;
						border-color: #dc2626;
					}
				}
			}
		}
	}
}

/* ==================== El-Table 布局样式 ==================== */
.schedule-table-container {
	flex: 1;
	background: var(--calendar-bg-primary, white);
	border-radius: 0 0 0 0; /* 移除底部圆角，因为分页组件在下面 */
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);

	/* 美化容器滚动条 */
	&::-webkit-scrollbar {
		width: 10px;
		height: 10px;
	}
	&::-webkit-scrollbar-track {
		background: var(--calendar-bg-secondary, #f8fafc);
		border-radius: 5px;
	}
	&::-webkit-scrollbar-thumb {
		background: var(--calendar-text-placeholder, #cbd5e1);
		border-radius: 5px;
		border: 2px solid var(--calendar-bg-secondary, #f8fafc);
		&:hover {
			background: var(--calendar-text-secondary, #94a3b8);
		}
		&:active {
			background: var(--calendar-text-primary, #64748b);
		}
	}
}

/* ==================== 分页组件样式 ==================== */
.pagination-container {
	background: var(--calendar-bg-secondary, var(--el-color-white));
	padding: 20px 32px;
	border-radius: 0 0 20px 20px;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
	border-top: 1px solid var(--el-border-color-light);
	display: flex;
	justify-content: center;
	align-items: center;
	transition: var(--el-transition-all);
	position: relative;
	z-index: 1600; /* 确保分页组件在批量排班提示框之上 */
}

.schedule-pagination {
	:deep(.el-pagination) {
		.el-pagination__total {
			color: var(--el-text-color-regular);
			font-weight: 500;
			font-size: 14px;
		}

		.el-pagination__sizes {
			.el-select {
				.el-input__wrapper {
					border-radius: 8px;
					border-color: var(--el-border-color);
					background: var(--el-color-white);

					&:hover {
						border-color: var(--el-color-primary);
					}
				}
			}
		}

		.btn-prev,
		.btn-next {
			background: var(--el-color-white);
			border: 1px solid var(--el-border-color);
			border-radius: 8px;
			color: var(--el-text-color-regular);
			font-weight: 500;
			transition: var(--el-transition-all);

			&:hover {
				background: var(--el-fill-color-light);
				border-color: var(--el-color-primary);
				color: var(--el-color-primary);
			}

			&:disabled {
				background: var(--el-fill-color-light);
				border-color: var(--el-border-color-light);
				color: var(--el-text-color-disabled);
				cursor: not-allowed;
			}
		}

		.el-pager {
			li {
				background: var(--el-color-white);
				border: 1px solid var(--el-border-color);
				border-radius: 8px;
				color: var(--el-text-color-regular);
				font-weight: 500;
				margin: 0 4px;
				transition: var(--el-transition-all);

				&:hover {
					background: var(--el-fill-color-light);
					border-color: var(--el-color-primary);
					color: var(--el-color-primary);
				}

				&.is-active {
					background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-dark-2) 100%);
					border-color: var(--el-color-primary);
					color: var(--el-color-white);
					font-weight: 600;
					box-shadow: 0 2px 4px var(--el-color-primary-light-7);

					&:hover {
						background: linear-gradient(135deg, var(--el-color-primary-dark-2) 0%, var(--el-color-primary-dark-2) 100%);
						transform: translateY(-1px);
						box-shadow: 0 4px 8px var(--el-color-primary-light-5);
					}
				}
			}
		}

		.el-pagination__jump {
			color: var(--el-text-color-regular);
			font-weight: 500;

			.el-input {
				.el-input__wrapper {
					border-radius: 8px;
					border-color: var(--el-border-color);
					background: var(--el-color-white);
					width: 50px;

					&:hover {
						border-color: var(--el-color-primary);
					}

					.el-input__inner {
						text-align: center;
						font-weight: 500;
						color: var(--el-text-color-primary);
					}
				}
			}
		}
	}
}
.range-selection-panel {
	position: fixed;
	background: var(--calendar-bg-primary, white);
	border-radius: 12px;
	box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
	border: 1px solid var(--calendar-border-color, #e5e7eb);
	width: 320px;
	z-index: 2000;
	animation: panelFadeIn 0.2s ease-out;
	transform-origin: top center;

	.el-card__header {
		background: #409eff;
		color: white;
		border-radius: 12px 12px 0 0;
		padding: 12px 16px;
		box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
		position: relative;

		&::after {
			content: '';
			position: absolute;
			bottom: 0;
			left: 0;
			right: 0;
			height: 1px;
			background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
		}
	}

	.el-card__body {
		padding: 16px;
	}
}
/* ==================== 暗黑模式支持 ==================== */
[data-theme='dark'] .pagination-container,
.dark .pagination-container {
	background: var(--next-color-primary);
	border-top-color: var(--next-border-color);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

[data-theme='dark'] .schedule-table-container,
.dark .schedule-table-container {
	background: var(--el-bg-color-page, #141414);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
	border: 1px solid var(--el-border-color-darker, #363637);
}

/* 暗黑模式 - 排班表头部 */
[data-theme='dark'] .schedule-header,
.dark .schedule-header {
	background: var(--el-bg-color, #1d1e1f);
	border-bottom: 1px solid var(--el-border-color-darker, #363637);
	color: var(--el-text-color-primary, #e5eaf3);
}

/* ==================== 标识说明样式 ==================== */
.legend-list {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.legend-item {
	display: flex;
	align-items: center;
	gap: 12px;
}

.legend-text {
	font-size: 13px;
	color: var(--el-text-color-primary, #303133);
}

/* 使对话框中的示例更紧凑，不影响原样式表现 */
.legend-sample {
	width: 80px;
	height: 40px;
	border: 1px solid var(--el-border-color, #e5e7eb);
	border-radius: 6px;
	background: var(--calendar-bg-primary, #fff);
	box-sizing: border-box;
}

[data-theme='dark'] .schedule-header .header-cell,
.dark .schedule-header .header-cell {
	background: linear-gradient(135deg, #1d1e1f 0%, #262727 100%);
	color: var(--el-text-color-primary, #e5eaf3);
	border-right: 1px solid var(--el-border-color-darker, #363637);
}

[data-theme='dark'] .schedule-header .header-cell:hover,
.dark .schedule-header .header-cell:hover {
	background: linear-gradient(135deg, #262727 0%, #2d2d2d 100%);
}

/* 暗黑模式 - 表格主体 */
[data-theme='dark'] .schedule-table,
.dark .schedule-table {
	background: var(--el-bg-color, #1d1e1f);
	color: var(--el-text-color-primary, #e5eaf3);
}

/* 暗黑模式 - 表格单元格 */
[data-theme='dark'] .schedule-table .schedule-cell,
.dark .schedule-table .schedule-cell {
	background: var(--el-bg-color, #1d1e1f);
	border-right: 1px solid var(--el-border-color-darker, #363637);
	border-bottom: 1px solid var(--el-border-color-darker, #363637);
	color: var(--el-text-color-primary, #e5eaf3);
}

[data-theme='dark'] .schedule-table .schedule-cell:hover,
.dark .schedule-table .schedule-cell:hover {
	background: var(--el-bg-color-overlay, #262727);
}

/* 暗黑模式 - 工作日单元格 */
[data-theme='dark'] .schedule-table .schedule-cell.workday,
.dark .schedule-table .schedule-cell.workday {
	background: var(--el-bg-color, #1d1e1f);
}

[data-theme='dark'] .schedule-table .schedule-cell.workday:hover,
.dark .schedule-table .schedule-cell.workday:hover {
	background: var(--el-bg-color-overlay, #262727);
}

/* 暗黑模式 - 休息日单元格 */
[data-theme='dark'] .schedule-table .schedule-cell.holiday,
.dark .schedule-table .schedule-cell.holiday {
	background: #2d1b1b;
	color: #f5c6c6;
}

[data-theme='dark'] .schedule-table .schedule-cell.holiday:hover,
.dark .schedule-table .schedule-cell.holiday:hover {
	background: #3d2525;
}

/* 暗黑模式 - 周末单元格 */
[data-theme='dark'] .schedule-table .schedule-cell.weekend,
.dark .schedule-table .schedule-cell.weekend {
	background: #1a1f2e;
	color: #c6d0f5;
}

[data-theme='dark'] .schedule-table .schedule-cell.weekend:hover,
.dark .schedule-table .schedule-cell.weekend:hover {
	background: #242938;
}

/* 暗黑模式 - 员工信息卡片 */
[data-theme='dark'] .employee-card,
.dark .employee-card {
	background: var(--calendar-bg-primary, #1f1f1f) !important;
	border: 1px solid var(--calendar-border-color, #424242) !important;
	color: var(--calendar-text-primary, #e5eaf3) !important;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4) !important;
}

[data-theme='dark'] .employee-card:hover,
.dark .employee-card:hover {
	background: var(--calendar-bg-hover, #3c3c3c) !important;
	border-color: var(--calendar-border-color, #424242) !important;
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5) !important;
	transform: translateX(1px) !important;

	.employee-code {
		background: var(--calendar-bg-tertiary, #404040) !important;
		color: var(--calendar-text-secondary, #9b9da1) !important;
	}
}

[data-theme='dark'] .employee-card .employee-name,
.dark .employee-card .employee-name {
	color: var(--calendar-text-primary, #e5eaf3) !important;
	font-weight: 600;
}

[data-theme='dark'] .employee-card .employee-code,
.dark .employee-card .employee-code {
	color: var(--calendar-text-secondary, #9b9da1) !important;
	background: var(--calendar-bg-secondary, #303030) !important;
}

[data-theme='dark'] .employee-card .employee-info,
.dark .employee-card .employee-info {
	color: var(--calendar-text-secondary, #9b9da1) !important;
}

[data-theme='dark'] .employee-card .employee-status,
.dark .employee-card .employee-status {
	color: var(--el-text-color-secondary, #909399);
}

/* 暗黑模式 - 员工卡片状态颜色 */
[data-theme='dark'] .employee-card .status-working,
.dark .employee-card .status-working {
	color: #67c23a;
	background: rgba(103, 194, 58, 0.1);
	border: 1px solid rgba(103, 194, 58, 0.2);
}

[data-theme='dark'] .employee-card .status-rest,
.dark .employee-card .status-rest {
	color: #f56c6c;
	background: rgba(245, 108, 108, 0.1);
	border: 1px solid rgba(245, 108, 108, 0.2);
}

[data-theme='dark'] .employee-card .status-leave,
.dark .employee-card .status-leave {
	color: #e6a23c;
	background: rgba(230, 162, 60, 0.1);
	border: 1px solid rgba(230, 162, 60, 0.2);
}

/* 暗黑模式 - 拖拽状态 */
[data-theme='dark'] .schedule-cell.dragging,
.dark .schedule-cell.dragging {
	background: rgba(64, 158, 255, 0.2) !important;
	border: 2px dashed var(--el-color-primary, #409eff) !important;
	transform: scale(1.02);
	box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}

[data-theme='dark'] .employee-card.dragging,
.dark .employee-card.dragging {
	background: var(--el-color-primary-light-8, #79bbff) !important;
	border: 2px solid var(--el-color-primary, #409eff) !important;
	transform: rotate(2deg) scale(1.05);
	box-shadow: 0 8px 24px rgba(64, 158, 255, 0.4);
	z-index: 1000;
	opacity: 0.9;
}

/* 暗黑模式 - 拖拽目标区域 */
[data-theme='dark'] .schedule-cell.drop-target,
.dark .schedule-cell.drop-target {
	background: rgba(103, 194, 58, 0.2) !important;
	border: 2px dashed var(--el-color-success, #67c23a) !important;
	animation: pulse-dark 1.5s infinite;
}

@keyframes pulse-dark {
	0% {
		box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.4);
	}
	70% {
		box-shadow: 0 0 0 10px rgba(103, 194, 58, 0);
	}
	100% {
		box-shadow: 0 0 0 0 rgba(103, 194, 58, 0);
	}
}

/* 暗黑模式 - 选中状态 */
[data-theme='dark'] .schedule-cell.selected,
.dark .schedule-cell.selected {
	background: rgba(64, 158, 255, 0.15) !important;
	border: 2px solid var(--el-color-primary, #409eff) !important;
	box-shadow: inset 0 0 0 2px rgba(64, 158, 255, 0.3);
}

[data-theme='dark'] .employee-card.selected,
.dark .employee-card.selected {
	background: var(--el-color-primary-light-9, #a0cfff) !important;
	border: 2px solid var(--el-color-primary, #409eff) !important;
	box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.3);
	transform: scale(1.02);
}

/* 暗黑模式 - 高亮状态 */
[data-theme='dark'] .schedule-cell.highlighted,
.dark .schedule-cell.highlighted {
	background: rgba(230, 162, 60, 0.15) !important;
	border: 1px solid var(--el-color-warning, #e6a23c) !important;
	box-shadow: inset 0 0 0 1px rgba(230, 162, 60, 0.3);
}

/* 暗黑模式 - 禁用状态 */
[data-theme='dark'] .schedule-cell.disabled,
.dark .schedule-cell.disabled {
	background: var(--el-bg-color-disabled, #2b2b2b) !important;
	color: var(--el-text-color-disabled, #6c6c6c) !important;
	cursor: not-allowed;
	opacity: 0.6;
}

[data-theme='dark'] .employee-card.disabled,
.dark .employee-card.disabled {
	background: var(--el-bg-color-disabled, #2b2b2b) !important;
	color: var(--el-text-color-disabled, #6c6c6c) !important;
	border-color: var(--el-border-color-disabled, #4a4a4a) !important;
	cursor: not-allowed;
	opacity: 0.6;
}

/* 暗黑模式 - 容器滚动条 */
[data-theme='dark'] .schedule-table-container::-webkit-scrollbar-track,
.dark .schedule-table-container::-webkit-scrollbar-track {
	background: #1a1b1e;
}

[data-theme='dark'] .schedule-table-container::-webkit-scrollbar-thumb,
.dark .schedule-table-container::-webkit-scrollbar-thumb {
	background: #404040;
	border: 2px solid var(--calendar-border-dark, #1a1b1e);
}

[data-theme='dark'] .schedule-table-container::-webkit-scrollbar-thumb:hover,
.dark .schedule-table-container::-webkit-scrollbar-thumb:hover {
	background: #525252;
}

[data-theme='dark'] .schedule-table-container::-webkit-scrollbar-thumb:active,
.dark .schedule-table-container::-webkit-scrollbar-thumb:active {
	background: #6b7280;
}

/* 暗黑模式 - el-table 内部滚动条 */
[data-theme='dark'] .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track,
.dark .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
	background: #1a1b1e;
}

[data-theme='dark'] .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb,
.dark .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
	background: #404040;
}

[data-theme='dark'] .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover,
.dark .schedule-el-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
	background: #525252;
}

/* ==================== 响应式设计 ==================== */

/* 超大屏幕 (≥1920px) */
@media screen and (min-width: 1920px) {
	.pagination-container {
		padding: 24px 40px;
	}

	.schedule-pagination {
		:deep(.el-pagination) {
			.el-pagination__total {
				font-size: 15px;
			}

			.btn-prev,
			.btn-next {
				padding: 8px 16px;
				font-size: 14px;
			}

			.el-pager li {
				min-width: 36px;
				height: 36px;
				line-height: 34px;
				font-size: 14px;
			}
		}
	}
}

/* 大屏幕 (1200px - 1919px) */
@media screen and (min-width: 1200px) and (max-width: 1919px) {
	.pagination-container {
		padding: 20px 32px;
	}

	.schedule-pagination {
		:deep(.el-pagination) {
			.el-pager li {
				min-width: 32px;
				height: 32px;
				line-height: 30px;
			}
		}
	}
}

/* 中等屏幕 (992px - 1199px) */
@media screen and (min-width: 992px) and (max-width: 1199px) {
	.pagination-container {
		padding: 16px 24px;
	}

	.schedule-pagination {
		:deep(.el-pagination) {
			.el-pagination__total {
				font-size: 13px;
			}

			.el-pager li {
				min-width: 30px;
				height: 30px;
				line-height: 28px;
				font-size: 13px;
				margin: 0 2px;
			}

			.btn-prev,
			.btn-next {
				padding: 6px 12px;
				font-size: 13px;
			}
		}
	}
}

/* 平板横屏 (768px - 991px) */
@media screen and (min-width: 768px) and (max-width: 991px) {
	.pagination-container {
		padding: 16px 20px;
		border-radius: 0 0 16px 16px;
	}

	.schedule-table-container {
		border-radius: 0;
	}

	.schedule-pagination {
		:deep(.el-pagination) {
			.el-pagination__total {
				font-size: 12px;
			}

			.el-pagination__sizes {
				margin-right: 8px;

				.el-select {
					.el-input__wrapper {
						width: 80px;
					}
				}
			}

			.el-pager li {
				min-width: 28px;
				height: 28px;
				line-height: 26px;
				font-size: 12px;
				margin: 0 1px;
			}

			.btn-prev,
			.btn-next {
				padding: 5px 10px;
				font-size: 12px;
			}

			.el-pagination__jump {
				.el-input {
					.el-input__wrapper {
						width: 45px;
					}
				}
			}
		}
	}
}

/* 平板竖屏和手机 (≤767px) */
@media screen and (max-width: 767px) {
	.pagination-container {
		padding: 12px 16px;
		border-radius: 0 0 12px 12px;
		flex-direction: column;
		gap: 12px;
	}

	.batch-mode-tips {
		top: 16px; /* 移动端顶部位置 */
		left: 50%;
		transform: translateX(-50%);

		.tips-content {
			width: calc(100vw - 32px); /* 移动端自适应宽度 */
			max-width: calc(100vw - 32px);
			min-width: 240px; /* 保持最小宽度 */
		}
	}

	.schedule-table-container {
		border-radius: 0;
	}

	.schedule-pagination {
		width: 100%;

		:deep(.el-pagination) {
			justify-content: center;
			flex-wrap: wrap;
			gap: 8px;

			.el-pagination__total {
				font-size: 12px;
				order: -1;
				width: 100%;
				text-align: center;
				margin-bottom: 8px;
			}

			.el-pagination__sizes {
				order: -1;
				margin: 0;

				.el-select {
					.el-input__wrapper {
						width: 70px;
					}
				}
			}

			.btn-prev,
			.btn-next {
				padding: 4px 8px;
				font-size: 11px;
				border-radius: 6px;
			}

			.el-pager {
				flex-wrap: wrap;
				justify-content: center;

				li {
					min-width: 24px;
					height: 24px;
					line-height: 22px;
					font-size: 11px;
					margin: 1px;
					border-radius: 6px;
				}
			}

			.el-pagination__jump {
				font-size: 11px;

				.el-input {
					.el-input__wrapper {
						width: 40px;
						height: 24px;

						.el-input__inner {
							font-size: 11px;
							line-height: 22px;
						}
					}
				}
			}
		}
	}
}

/* 小屏手机 (≤480px) */
@media screen and (max-width: 480px) {
	.pagination-container {
		padding: 10px 12px;
		border-radius: 0 0 8px 8px;
	}

	.batch-mode-tips {
		top: 12px; /* 小屏幕顶部位置 */

		.tips-content {
			width: calc(100vw - 24px); /* 小屏幕自适应宽度 */
			max-width: calc(100vw - 24px);
			min-width: 200px; /* 小屏幕最小宽度 */
		}
	}

	.schedule-pagination {
		:deep(.el-pagination) {
			.el-pagination__total {
				font-size: 11px;
			}

			.el-pagination__sizes {
				.el-select {
					.el-input__wrapper {
						width: 65px;
					}
				}
			}

			.btn-prev,
			.btn-next {
				padding: 3px 6px;
				font-size: 10px;
			}

			.el-pager {
				li {
					min-width: 22px;
					height: 22px;
					line-height: 20px;
					font-size: 10px;
				}
			}

			.el-pagination__jump {
				font-size: 10px;

				.el-input {
					.el-input__wrapper {
						width: 35px;
						height: 22px;

						.el-input__inner {
							font-size: 10px;
							line-height: 20px;
						}
					}
				}
			}
		}
	}
}

/* El-Table 基础样式 */
.schedule-el-table {
	width: 100%;
	font-family: inherit;

	/* 重置 El-Table 默认样式 */
	:deep(.el-table__header) {
		background: var(--calendar-bg-secondary, #fafbfc);
	}

	:deep(.el-table__header-wrapper) {
		background: var(--calendar-bg-secondary, #fafbfc);
	}

	:deep(.el-table__body-wrapper) {
		background: var(--calendar-bg-primary, white);
		/* 美化 el-table 内部滚动条 */
		&::-webkit-scrollbar {
			width: 8px;
			height: 8px;
		}
		&::-webkit-scrollbar-track {
			background: var(--calendar-bg-secondary, #f1f5f9);
			border-radius: 4px;
		}
		&::-webkit-scrollbar-thumb {
			background: var(--calendar-text-placeholder, #cbd5e1);
			border-radius: 4px;
			&:hover {
				background: var(--calendar-text-secondary, #94a3b8);
			}
		}
	}

	/* 表头样式 */
	:deep(.el-table__header th) {
		background: var(--calendar-bg-secondary, #fafbfc) !important;
		border-bottom: 1px solid var(--calendar-border-color, #f1f5f9) !important;
		border-right: 1px solid var(--calendar-border-color, #f1f5f9) !important;
		padding: 0 !important;
		font-weight: 600;
		color: var(--calendar-text-primary, #475569);
	}

	/* 员工信息表头 */
	:deep(.employee-column .cell) {
		padding: 0 !important;
	}

	.employee-header-content {
		padding: 18px 16px;
		text-align: center;
		font-weight: 700;
		color: var(--calendar-text-primary, #1f2937);
		font-size: 15px;
		background: var(--calendar-bg-secondary, #fafbfc);
		border-right: 1px solid var(--calendar-border-color, #f1f5f9);
		letter-spacing: -0.025em;
	}

	/* 日期表头样式 */
	.date-header-content {
		padding: 14px 10px;
		text-align: center;
		background: var(--calendar-bg-secondary, #fafbfc);
		border-right: 1px solid var(--calendar-border-color, #f1f5f9);
		transition: all 0.2s ease;
		position: relative;

		&.date-header-selectable {
			cursor: pointer;
			user-select: none;

			&:hover {
				background: rgba(59, 130, 246, 0.05);

				.date-number {
					color: #2563eb;
				}
			}
		}

		&.is-column-hovered {
			background: rgba(59, 130, 246, 0.08);

			.date-number {
				color: #1d4ed8;
				transform: scale(1.05);
			}
		}

		&.is-column-selected {
			background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
			color: white;
			box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);

			.date-number {
				color: white;
				font-weight: 800;
			}

			.date-weekday {
				color: rgba(255, 255, 255, 0.95);
			}

			.column-selection-badge {
				opacity: 1;
			}
		}

		&.is-weekend {
			background: var(--calendar-bg-hover, #fafafa);

			.date-number {
				color: #a78787;
			}

			.date-weekday {
				color: #a78787;
			}

			&.date-header-selectable:hover {
				background: rgba(59, 130, 246, 0.05);

				.date-number {
					color: #2563eb;
				}
			}
		}

		&.is-today {
			background: #5a8fd9;
			color: white;
			border-radius: 6px;
			margin: 2px;

			.date-number {
				color: white;
				font-weight: 700;
			}

			.date-weekday {
				color: rgba(255, 255, 255, 0.9);
			}

			&.is-column-selected {
				background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
			}
		}

		.date-number {
			font-size: 19px;
			font-weight: 700;
			color: var(--calendar-text-primary, #111827);
			line-height: 1.1;
			letter-spacing: -0.03em;
			transition: all 0.2s ease;
		}

		.date-weekday {
			font-size: 11px;
			color: var(--calendar-text-secondary, #9ca3af);
			margin-top: 4px;
			font-weight: 500;
			opacity: 0.85;
			transition: all 0.2s ease;
		}

		.column-selection-badge {
			position: absolute;
			top: 4px;
			right: 4px;
			background: #ef4444;
			color: white;
			font-size: 10px;
			font-weight: 600;
			padding: 2px 6px;
			border-radius: 10px;
			min-width: 18px;
			text-align: center;
			box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
			opacity: 0.95;
			animation: badgePulse 2s ease-in-out infinite;
		}
	}

	@keyframes badgePulse {
		0%,
		100% {
			transform: scale(1);
		}
		50% {
			transform: scale(1.1);
		}
	}

	/* 表格行样式 */
	:deep(.el-table__row) {
		transition: all 0.15s ease;

		&:hover {
			background: rgba(59, 130, 246, 0.02) !important;
		}

		&.is-selected {
			background: rgba(59, 130, 246, 0.04) !important;
		}
	}

	/* 表格单元格基础样式 */
	:deep(.el-table__body td) {
		padding: 0 !important;
		border-bottom: 1px solid var(--calendar-border-color, #f1f5f9) !important;
		border-right: 1px solid var(--calendar-border-color, #f1f5f9) !important;
		vertical-align: top;
	}

	/* 员工信息单元格 */
	:deep(.employee-column .cell) {
		padding: 0 !important;
		border-right: 1px solid var(--calendar-border-color, #e2e8f0) !important;
	}
}

/* 员工表头内容样式 */
.employee-header-content {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 0 8px;

	.select-all-checkbox {
		:deep(.el-checkbox__inner) {
			width: 18px;
			height: 18px;
			border-radius: 6px;
			border: 2px solid var(--calendar-border-color, #d1d5db);
			transition: all 0.2s ease;

			&:hover {
				border-color: var(--el-color-primary, #409eff);
			}
		}

		:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
			background-color: var(--el-color-primary, #409eff);
			border-color: var(--el-color-primary, #409eff);
		}

		:deep(.el-checkbox__input.is-indeterminate .el-checkbox__inner) {
			background-color: var(--el-color-primary, #409eff);
			border-color: var(--el-color-primary, #409eff);
		}
	}

	.header-text {
		font-weight: 600;
		font-size: 14px;
		color: var(--calendar-text-primary, #374151);
	}
}

/* 员工卡片样式 - 适配 El-Table */
.employee-card {
	padding: 14px 12px;
	display: flex;
	align-items: center;
	gap: 12px;
	height: 64px; /* 固定高度与排班单元格一致 */
	transition: all 0.2s ease;
	position: relative;

	&.clickable {
		cursor: pointer;

		&:hover {
			background: #f8fafc;
			transform: translateX(1px);
			box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
		}
	}

	// 批量模式样式
	&.batch-mode {
		padding-left: 8px; // 批量模式时减少左侧内边距，为复选框腾出空间

		&.is-selected {
			background: linear-gradient(135deg, #e8f1fa 0%, #f0f7ff 100%);
			border-left: 3px solid var(--el-color-primary, #409eff);

			.employee-avatar .avatar-placeholder {
				background: linear-gradient(135deg, #409eff 0%, #5a8fd9 100%);
				box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
			}

			&:hover {
				background: linear-gradient(135deg, #dce9f7 0%, #e8f1fa 100%);
			}
		}
	}

	// 人员复选框样式
	.person-checkbox {
		flex-shrink: 0;
		margin-right: 0;

		:deep(.el-checkbox__inner) {
			width: 18px;
			height: 18px;
			border-radius: 6px;
			border: 2px solid var(--calendar-border-color, #d1d5db);
			transition: all 0.2s ease;

			&:hover {
				border-color: var(--el-color-primary, #409eff);
				transform: scale(1.1);
			}
		}

		:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
			background-color: var(--el-color-primary, #409eff);
			border-color: var(--el-color-primary, #409eff);
		}

		:deep(.el-checkbox__label) {
			display: none; // 隐藏复选框标签
		}
	}

	.employee-avatar {
		position: relative;

		.avatar-placeholder {
			width: 40px;
			height: 40px;
			border-radius: 10px;
			background: #5a8fd9;
			color: white;
			display: flex;
			align-items: center;
			justify-content: center;
			font-weight: 700;
			font-size: 14px;
		}

		.selection-indicator {
			position: absolute;
			top: -2px;
			right: -2px;
			width: 16px;
			height: 16px;
			background: #16a34a;
			border-radius: 50%;
			color: white;
			font-size: 10px;
			display: flex;
			align-items: center;
			justify-content: center;
			font-weight: bold;
			border: 2px solid white;
		}
	}

	.employee-info {
		flex: 1;

		.employee-name {
			font-weight: 700;
			color: #111827;
			font-size: 16px;
			line-height: 1.3;
			letter-spacing: -0.02em;
		}

		.employee-code {
			font-size: 11px;
			color: #9ca3af;
			margin-top: 4px;
			font-weight: 500;
			opacity: 0.85;
		}
	}

	.employee-stats {
		.stat-item {
			text-align: center;

			.stat-label {
				font-size: 10px;
				color: #9ca3af;
				display: block;
			}

			.stat-value {
				font-size: 14px;
				font-weight: 600;
				color: #4a5568;
				display: block;
				margin-top: 2px;
			}

			// 异常状态样式
			&.abnormal {
				.stat-label {
					color: #dc2626; // 使用更鲜艳的红色标签
				}

				.stat-value {
					color: #dc2626; // 使用更鲜艳的红色数值
					background-color: #fef2f2; // 浅红色背景
					border-radius: 4px;
					padding: 2px 6px;
				}
			}
		}
	}
}

/* 排班单元格样式 - 适配 El-Table */
.schedule-cell {
	height: 64px;
	background: var(--calendar-bg-primary, white);
	position: relative;
	transition: all 0.15s ease;
	cursor: default;
	border: 1px solid #f3f4f6;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);

	&:hover {
		box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
		transform: translateY(-1px);
	}

	/* 已计算或已上班的班次 - 灰色显示 */
	&.is-completed-or-past {
		background: #f3f4f6 !important;
		opacity: 0.75;

		.cell-content {
			.shift-content {
				.shift-main {
					filter: grayscale(0.4);

					.shift-group {
						color: #6b7280 !important;
						font-weight: 500;
					}

					.shift-name {
						color: #6b7280 !important;
						font-weight: 500;
					}

					.shift-time {
						color: #9ca3af !important;
					}
				}

				.empty-content {
					.empty-text {
						color: #9ca3af !important;
					}
				}
			}
		}

		/* 已完成的班次，降低标识的显眼程度 */
		.shift-type-indicator {
			opacity: 0.6;
		}

		.abnormal-indicator {
			opacity: 0.7;
		}

		/* 但是异常排班的红色边框不受影响，保持醒目 */
		&.abnormal-schedule {
			border: 2px solid #ef4444 !important; /* 使用最亮的红色 */
			background: #fef2f2 !important;
			box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.3) !important;
			filter: none !important; /* 移除灰度滤镜 */

			&:hover {
				box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.5) !important;
			}
		}
	}

	/* 左侧异常边（旷工） */
	.cell__side {
		position: absolute;
		top: 0;
		left: 0;
		width: 3px;
		height: 100%;
		background: #ef4444;
		border-radius: 2px;
		z-index: 11;
	}

	/* 角落异常形状 */
	.corner-flag {
		position: absolute;
		width: 0;
		height: 0;
		border-style: solid;
		z-index: 11;
	}

	/* 左上角三角：迟到 */
	.corner-flag--late {
		top: 0;
		left: 0;
		border-width: 8px 8px 0 0;
		border-color: #f59e0b transparent transparent transparent;
	}

	/* 右下角三角：早退 */
	.corner-flag--early {
		bottom: 0;
		right: 0;
		border-width: 0 0 8px 8px;
		border-color: transparent transparent #f59e0b transparent;
	}

	/* 左下角小方块：缺卡 */
	.cell__badge {
		position: absolute;
		bottom: 4px;
		left: 4px;
		width: 8px;
		height: 8px;
		border-radius: 2px;
		z-index: 11;
	}

	.cell__badge--missing {
		background: #fbbf24;
	}

	/* 底部状态点容器 */
	.cell__dots {
		position: absolute;
		bottom: 4px;
		left: 50%;
		transform: translateX(-50%);
		display: flex;
		gap: 4px;
		z-index: 11;
	}

	.cell__dots .dot {
		width: 6px;
		height: 6px;
		border-radius: 50%;
	}

	.dot--leave {
		background: #3b82f6; /* 蓝 */
	}

	.dot--trip {
		background: #8b5cf6; /* 紫 */
	}

	.dot--ot {
		background: #06b6d4; /* 青 */
	}

	.dot--offinlieu {
		background: #10b981; /* 青绿 */
	}

	/* 周末样式 */
	&.is-weekend {
		background: #fefefe;
	}

	/* 单元格内容 */
	.cell-content {
		padding: 10px;
		height: 100%;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;

		.shift-content {
			text-align: center;
			width: 100%;
			display: flex;
			flex-direction: column;
			gap: 3px;
			position: relative;

			.shift-group {
				font-size: 12px;
				line-height: 1.3;
				font-weight: 600;
				color: #2d3748;
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
			}

			/* 异常排班标识 - 统一定位在左上角 */
			.abnormal-indicator {
				position: absolute;
				top: -8px;
				left: -8px;
				z-index: 10;

				.abnormal-icon {
					display: inline-flex;
					align-items: center;
					justify-content: center;
					width: 16px;
					height: 16px;
					background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
					color: white;
					border-radius: 4px;
					font-size: 10px;
					font-weight: 600;
					box-shadow:
						0 2px 6px rgba(239, 68, 68, 0.4),
						0 1px 3px rgba(0, 0, 0, 0.1);
					border: 1px solid rgba(255, 255, 255, 0.2);
					animation: pulseWarning 2s infinite;
					transition: all 0.3s ease;

					&:hover {
						transform: scale(1.1);
						box-shadow: 0 4px 12px rgba(239, 68, 68, 0.6);
					}

					i {
						font-size: 9px;
						margin: 0;
						line-height: 1;
					}
				}
			}

			/* 班次类型标识 - 统一定位在右上角 */
			.shift-type-indicator {
				position: absolute;
				top: -10px;
				right: -6px;
				z-index: 20;
				display: inline-flex;
				align-items: center;
				justify-content: center;
				width: 20px;
				height: 20px;
				font-size: 11px;
				font-weight: 700;
				letter-spacing: -0.02em;
				border-radius: 50%;
				border: 2px solid white;
				box-shadow:
					0 3px 8px rgba(0, 0, 0, 0.25),
					0 1px 4px rgba(0, 0, 0, 0.15);
				backdrop-filter: blur(4px);
				transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
				pointer-events: none;
				line-height: 1;
				animation: pulseAttention 2s ease-in-out infinite;

				&:hover {
					transform: scale(1.2);
					box-shadow:
						0 4px 12px rgba(0, 0, 0, 0.35),
						0 2px 6px rgba(0, 0, 0, 0.2);
				}
			}

			/* 班次类型标签 */
			.shift-type-badge {
				position: absolute;
				top: -2px;
				left: -2px;
				z-index: 9;

				.shift-type-text {
					display: inline-block;
					padding: 1px 4px;
					font-size: 8px;
					line-height: 1.2;
					border-radius: 8px;
					font-weight: 600;
					white-space: nowrap;
					color: white;
					text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
					box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
				}

				/* 不同班次类型的颜色 - 柔和配色 */
				&.normal-work .shift-type-text {
					background: #5d9673;
				}

				&.leave .shift-type-text {
					background: #c9a857;
				}

				&.compensatory-leave .shift-type-text {
					background: #5a8fd9;
				}

				&.substitute .shift-type-text {
					background: #d8915b;
				}

				&.shift-change .shift-type-text {
					background: #8e88c4;
				}

				&.overtime .shift-type-text {
					background: #c07676;
				}

				&.rest-day .shift-type-text {
					background: #7c8ea6;
				}

				&.other-type .shift-type-text {
					background: #848b95;
				}

				/* 异常排班的特殊样式 */
				&.abnormal-schedule .shift-type-text {
					border: 2px solid #ef4444; /* 使用最亮的红色 */
					background: #fef2f2 !important;
					color: #dc2626 !important; /* 使用更深的鲜红色文字 */
					text-shadow: none !important;
					animation: pulseWarning 2s infinite;
				}
			}

			@keyframes pulseWarning {
				0%,
				100% {
					opacity: 1;
				}
				50% {
					opacity: 0.7;
				}
			}

			.shift-main {
				display: flex;
				flex-direction: column;
				align-items: center;
				gap: 4px;
				// padding: 4px 8px;

				.shift-type {
					font-size: 16px;
					font-weight: 700;
					color: #1a202c;
					line-height: 1.4;
					letter-spacing: -0.01em;
					white-space: nowrap;
					overflow: hidden;
					text-overflow: ellipsis;
					max-width: 100%;
				}

				.schedule-type {
					font-size: 11px;
					color: #4a5568;
					line-height: 1.3;
					margin-top: 1px;
					font-weight: 500;
					white-space: nowrap;
					overflow: hidden;
					text-overflow: ellipsis;
					max-width: 100%;
				}
			}

			.shift-status {
				font-size: 10px;
				padding: 1px 4px;
				border-radius: 2px;
				line-height: 1.2;

				&.正常 {
					background-color: #e8f5e8;
					color: #52c41a;
				}

				&.异常 {
					background-color: var(--calendar-bg-primary, #fff2f0);
					color: var(--shift-other-text, #ff4d4f);
					border: 1px solid var(--shift-other-border, #ff4d4f);
				}

				&.迟到 {
					background-color: var(--calendar-bg-primary, #fff7e6);
					color: var(--shift-rest-text, #fa8c16);
					border: 1px solid var(--shift-rest-border, #fa8c16);
				}

				&.早退 {
					background-color: var(--calendar-bg-primary, #fff7e6);
					color: var(--shift-rest-text, #fa8c16);
					border: 1px solid var(--shift-rest-border, #fa8c16);
				}
			}

			.shift-name {
				font-size: 16px;
				font-weight: 700;
				color: #1a202c;
				line-height: 1.4;
				// margin: 3px 18px 3px 14px; /* 为左右两侧的标识留出空间 */
				letter-spacing: -0.01em;
				text-align: center;
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
			}

			.shift-time {
				font-size: 11px;
				color: #4a5568;
				line-height: 1.3;
				font-weight: 500;
			}
		}

		.empty-content {
			display: flex;
			align-items: center;
			justify-content: center;
			height: 100%;

			.empty-text {
				color: #d1d5db;
				font-size: 12px;
				font-weight: 500;
				opacity: 0.6;
			}
		}
	}

	/* 班次类型样式 - 简洁配色 */
	&.normal-shift {
		background: #f0f4f8;
		border-radius: 6px;
		margin: 1px;
		border-left: 3px solid #7c8ea6;

		.shift-name {
			color: #2d3748 !important;
		}

		.shift-time {
			color: #718096 !important;
		}
	}

	&.leave-shift {
		background: #f0f4f8;
		border-radius: 6px;
		margin: 1px;
		border-left: 3px solid #7c8ea6;

		.shift-name {
			color: #2d3748 !important;
		}

		.shift-time {
			color: #718096 !important;
		}
	}

	&.time-off-shift {
		background: #f7f8fa;
		border-radius: 6px;
		margin: 1px;
		border-left: 3px solid #a0aec0;

		.shift-name {
			color: #2d3748 !important;
		}

		.shift-time {
			color: #718096 !important;
		}
	}

	&.rest-shift {
		background: var(--shift-rest-bg, #fef8ed);
		border-radius: 6px;
		color: var(--shift-rest-text, inherit);
		margin: 1px;
		border-left: 3px solid #d9a95b;

		.shift-name {
			color: #744210 !important;
		}

		.shift-time {
			color: #975a16 !important;
		}

		/* 休班专用样式 - 已统一到 .shift-main，此处注释 */
		/* .rest-day {
			display: flex;
			justify-content: center;
			align-items: center;
			height: 100%;
			width: 100%;

			.rest-text {
				font-size: 18px;
				font-weight: 600;
				color: #92400e !important;
				text-align: center;
			}
		} */
	}

	&.other-shift {
		background: #f7f8fa;
		border-radius: 6px;
		margin: 1px;
		border-left: 3px solid #a0aec0;

		.shift-name {
			color: #2d3748 !important;
		}

		.shift-time {
			color: #718096 !important;
		}
	}

	/* 使用DisplayColor的通用有排班样式 - 统一背景色 */
	&.has-schedule {
		position: relative;
		margin: 1px;
		border-radius: 6px;
		border-left: 3px solid rgba(0, 0, 0, 0.15);

		.shift-name {
			color: #1a202c !important;
			font-weight: 700;
			font-size: 16px;
		}

		.shift-time {
			color: #4a5568 !important;
			font-size: 11px;
		}

		.shift-type {
			color: #2d3748 !important;
		}

		.schedule-type {
			color: #718096 !important;
		}

		.shift-group {
			color: #2d3748 !important;
			font-weight: 600;
			font-size: 12px;
		}

		.shift-info {
			color: #4a5568 !important;
		}
	}

	/* 没有接口颜色时的默认排班样式 */
	&.has-schedule-default {
		background: #f0f4f8;
		border-radius: 6px;
		margin: 1px;
		border-left: 3px solid #7c8ea6;

		.shift-name {
			color: #1a202c !important;
			font-weight: 700;
			font-size: 16px;
		}

		.shift-time {
			color: #4a5568 !important;
			font-size: 11px;
		}

		.shift-type {
			color: #2d3748 !important;
		}

		.schedule-type {
			color: #718096 !important;
		}

		.shift-group {
			color: #2d3748 !important;
			font-weight: 600;
			font-size: 12px;
		}

		.shift-info {
			color: #4a5568 !important;
		}
	}

	/* 批量模式样式 */
	&.batch-mode-cell {
		cursor: crosshair;
		user-select: none;
	}

	&.selected-cell {
		background: rgba(90, 143, 217, 0.1) !important;
		border: 2px solid #5a8fd9 !important;
		box-shadow: 0 0 0 1px rgba(90, 143, 217, 0.2);
		transform: scale(1.02);
		transition: all 0.2s ease;

		.shift-name {
			font-weight: 600;
			color: #2d3748 !important;
		}

		.shift-time {
			color: #4a5568 !important;
		}

		.empty-text {
			color: #5a8fd9 !important;
			font-weight: 500;
		}
	}

	/* 拖拽选择预览样式 */
	&.drag-selecting {
		background: rgba(90, 143, 217, 0.05) !important;
		border: 1px dashed #5a8fd9 !important;
		transition: all 0.15s ease;

		&::after {
			content: '';
			position: absolute;
			top: 0;
			left: 0;
			right: 0;
			bottom: 0;
			background: repeating-linear-gradient(45deg, transparent, transparent 3px, rgba(90, 143, 217, 0.08) 3px, rgba(90, 143, 217, 0.08) 6px);
			pointer-events: none;
			border-radius: 4px;
		}
	}

	/* 选择范围起点 */
	&.range-start {
		border-top-left-radius: 8px !important;
		border-bottom-left-radius: 8px !important;

		&::before {
			content: '';
			position: absolute;
			top: -1px;
			left: -1px;
			width: 6px;
			height: calc(100% + 2px);
			background: #5a8fd9;
			border-radius: 8px 0 0 8px;
			z-index: 10;
		}
	}

	/* 选择范围终点 */
	&.range-end {
		border-top-right-radius: 8px !important;
		border-bottom-right-radius: 8px !important;

		&::after {
			content: '';
			position: absolute;
			top: -1px;
			right: -1px;
			width: 6px;
			height: calc(100% + 2px);
			background: #7c9bc9;
			border-radius: 0 8px 8px 0;
			z-index: 10;
		}
	}

	/* 选择范围中间部分 */
	&.in-selection-range {
		background: rgba(90, 143, 217, 0.06) !important;
		border-top: 2px solid rgba(90, 143, 217, 0.4) !important;
		border-bottom: 2px solid rgba(90, 143, 217, 0.4) !important;
		border-right: none !important;
	}

	/* 异常状态 */
	&.abnormal {
		border: 2px solid #ef4444 !important; /* 使用最亮的红色 */
		background: #fef2f2 !important;
	}

	/* 具有异常排班的单元格整体样式 */
	&.abnormal-schedule {
		background: #fef2f2 !important;
		border: 3px solid #ef4444 !important; /* 加粗到3px，使用最亮的红色 */
		box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.3) !important;

		&:hover {
			box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.5) !important;
		}
	}

	/* 已完成状态 + 异常排班：红色边框优先级最高 */
	&.is-completed-or-past.abnormal-schedule {
		background: #fef2f2 !important;
		border: 3px solid #ef4444 !important; /* 加粗到3px，使用最亮的红色 */
		box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.3) !important;
		opacity: 1 !important; /* 不降低透明度 */
		filter: none !important; /* 不应用灰度滤镜 */

		&:hover {
			box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.5) !important;
		}

		/* 异常排班内部内容也不灰化 */
		.cell-content .shift-content .shift-main {
			filter: none !important;
		}
	}

	/* 批量模式样式 */
	&.batch-mode {
		cursor: crosshair;
		user-select: none;
	}
}

/* ==================== 暗黑模式完整样式适配 ==================== */
[data-theme='dark'],
.dark {
	/* 日历容器暗黑模式样式 */
	.schedule-calendar {
		background: var(--next-bg-main-color, #1f1f1f);
		color: var(--next-text-color-regular, #e5eaf3);
	}

	/* 表格暗黑模式样式 */
	.schedule-table {
		background: var(--next-bg-main-color, #1f1f1f);
		border: 1px solid var(--next-border-color, #424242);

		/* 表头样式 */
		.schedule-header {
			background: var(--next-color-primary, #303030);
			border-bottom: 2px solid var(--next-border-color, #424242);

			.header-cell {
				background: var(--next-color-primary, #303030);
				color: var(--next-text-color-regular, #e5eaf3);
				border-right: 1px solid var(--next-border-color, #424242);

				&.employee-header {
					background: var(--next-color-primary, #303030);
				}

				&.date-header {
					background: var(--next-color-primary, #303030);

					.date-text {
						color: var(--next-text-color-regular, #e5eaf3);
					}

					.day-text {
						color: var(--next-text-color-placeholder, #9b9da1);

						&.weekend {
							color: #f87171;
						}

						&.today {
							color: #60a5fa;
						}
					}
				}
			}
		}

		/* 表体样式 */
		.schedule-body {
			background: var(--next-bg-main-color, #1f1f1f);

			.schedule-row {
				border-bottom: 1px solid var(--next-border-color, #424242);

				&:hover {
					background: var(--next-color-hover, #3c3c3c);
				}
			}
		}
	}

	/* 员工信息卡片暗黑模式 */
	.employee-cell {
		background: var(--next-color-primary, #303030);
		border-right: 1px solid var(--next-border-color, #424242);

		&:hover {
			background: var(--next-color-hover, #3c3c3c);
		}

		.employee-card {
			.employee-info {
				.employee-name {
					color: var(--next-text-color-regular, #e5eaf3);
				}

				.employee-code {
					color: var(--next-text-color-placeholder, #9b9da1);
				}
			}

			.employee-stats {
				.stat-item {
					background: var(--next-color-hover, #3c3c3c);
					border: 1px solid var(--next-border-color, #424242);

					.stat-label {
						color: var(--next-text-color-placeholder, #9b9da1);
					}

					.stat-value {
						color: #60a5fa;
					}

					// 暗色主题下的异常状态样式
					&.abnormal {
						.stat-label {
							color: #f87171; // 暗色主题红色标签
						}

						.stat-value {
							color: #f87171; // 暗色主题红色数值
							background-color: rgba(239, 68, 68, 0.1); // 暗色主题浅红色背景
							border-radius: 4px;
							padding: 2px 6px;
						}
					}
				}
			}
		}
	}

	/* 排班单元格暗黑模式样式 */
	.schedule-cell {
		background: var(--next-bg-main-color, #1f1f1f);
		border-right: 1px solid var(--next-border-color, #424242);

		&.is-weekend {
			background: var(--next-color-primary, #303030);
		}

		.cell-content {
			.shift-content {
				.shift-main {
					.shift-type {
						color: var(--next-text-color-regular, #e5eaf3);
					}

					.schedule-type {
						color: var(--next-text-color-secondary, #b3b3b3);
					}
				}

				.shift-status {
					&.正常 {
						background-color: rgba(82, 196, 26, 0.2);
						color: #95de64;
					}

					&.异常 {
						background-color: rgba(255, 77, 79, 0.2);
						color: #ff7875;
					}

					&.迟到,
					&.早退 {
						background-color: rgba(250, 140, 22, 0.2);
						color: #ffa940;
					}
				}

				.shift-name {
					color: var(--next-text-color-regular, #e5eaf3);
				}

				.shift-time {
					color: var(--next-text-color-placeholder, #9b9da1);
				}

				.shift-group {
					color: var(--next-text-color-regular, #e5eaf3);
				}

				.shift-info {
					color: var(--next-text-color-regular, #e5eaf3);
				}
			}

			.empty-content .empty-text {
				color: var(--next-text-color-placeholder, #9b9da1);
			}
		}

		/* 班次类型暗黑模式样式 */
		&.normal-shift {
			background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%);

			.shift-name {
				color: #dbeafe !important;
			}

			.shift-time {
				color: #93c5fd !important;
			}
		}

		&.leave-shift {
			background: linear-gradient(135deg, #14532d 0%, #166534 100%);

			.shift-name {
				color: #dcfce7 !important;
			}

			.shift-time {
				color: #86efac !important;
			}
		}

		&.time-off-shift {
			background: linear-gradient(135deg, #334155 0%, #475569 100%);

			.shift-name {
				color: #f1f5f9 !important;
			}

			.shift-time {
				color: #cbd5e1 !important;
			}
		}

		&.rest-shift {
			background: linear-gradient(135deg, #92400e 0%, #b45309 100%);

			.shift-name {
				color: #fef3c7 !important;
			}

			.shift-time {
				color: #fcd34d !important;
			}

			/* 深色模式下的休班专用样式 - 已统一，此处注释 */
			/* .rest-day {
				.rest-text {
					color: #fef3c7 !important;
				}
			} */
		}

		&.other-shift {
			background: linear-gradient(135deg, #831843 0%, #be185d 100%);

			.shift-name {
				color: #fdf2f8 !important;
			}

			.shift-time {
				color: #f9a8d4 !important;
			}
		}

		/* 没有接口颜色时的默认排班样式 - 暗黑模式 */
		&.has-schedule-default {
			background: linear-gradient(135deg, #374151 0%, #4b5563 100%);

			.shift-name {
				color: #e5e7eb !important;
				font-weight: 600;
			}

			.shift-time {
				color: #d1d5db !important;
			}

			.shift-type {
				color: #f3f4f6 !important;
			}

			.schedule-type {
				color: #d1d5db !important;
			}

			.shift-group {
				color: #e5e7eb !important;
				font-weight: 600;
			}

			.shift-info {
				color: #e5e7eb !important;
				font-weight: 600;
			}
		}

		/* 选择状态暗黑模式 */
		&.selected-cell {
			background: rgba(99, 102, 241, 0.15) !important;
			border: 2px solid var(--shift-primary-border, #6366f1) !important;
			box-shadow: 0 0 0 1px rgba(99, 102, 241, 0.4);

			.shift-name {
				color: #a5b4fc !important;
			}

			.shift-time {
				color: #818cf8 !important;
			}

			.empty-text {
				color: #6366f1 !important;
			}
		}

		&.drag-selecting {
			background: rgba(99, 102, 241, 0.08) !important;
			border: 1px dashed #6366f1 !important;

			&::after {
				background: repeating-linear-gradient(45deg, transparent, transparent 3px, rgba(99, 102, 241, 0.15) 3px, rgba(99, 102, 241, 0.15) 6px);
			}
		}

		&.range-start::before {
			background: #6366f1;
		}

		&.range-end::after {
			background: #8b5cf6;
		}

		&.in-selection-range {
			background: rgba(99, 102, 241, 0.1) !important;
			border-top: 2px solid rgba(99, 102, 241, 0.6) !important;
			border-bottom: 2px solid rgba(99, 102, 241, 0.6) !important;
		}

		/* 异常状态暗黑模式 */
		&.abnormal {
			border: 2px solid var(--shift-conflict-border, #ef4444) !important;
			background: linear-gradient(135deg, #7f1d1d 0%, #991b1b 100%) !important;
		}

		/* 具有异常排班的单元格整体样式 - 暗黑模式 */
		&.abnormal-schedule {
			background: rgba(127, 29, 29, 0.3) !important;
			border: 3px solid #ef4444 !important; /* 加粗到3px，使用最亮的红色 */
			box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.5) !important;

			&:hover {
				background: rgba(127, 29, 29, 0.4) !important;
				box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.7) !important;
			}

			/* 暗黑模式下的异常排班内容样式 */
			.shift-content {
				.abnormal-indicator {
					.abnormal-icon {
						background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
						color: white;
						box-shadow:
							0 2px 8px rgba(248, 113, 113, 0.5),
							0 1px 3px rgba(0, 0, 0, 0.3);
						border: 1px solid rgba(255, 255, 255, 0.15);

						&:hover {
							background: linear-gradient(135deg, #fca5a5 0%, #f87171 100%);
							box-shadow: 0 4px 16px rgba(248, 113, 113, 0.7);
						}
					}
				}

				/* 暗黑模式下的班次类型标识 */
				.shift-type-indicator {
					border: 1px solid rgba(255, 255, 255, 0.15);
					box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
					backdrop-filter: blur(6px);

					&:hover {
						transform: scale(1.1);
						box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
					}
				}

				.shift-type-badge {
					&.abnormal-schedule .shift-type-text {
						border: 2px solid #ef4444;
						background: rgba(239, 68, 68, 0.2) !important;
						color: #fca5a5 !important;
						text-shadow: none !important;
					}
				}

				.shift-main {
					.shift-group,
					.shift-name,
					.shift-info {
						color: #fca5a5 !important;
					}
				}
			}
		}

		/* 暗黑模式：已完成状态 + 异常排班，红色边框优先级最高 */
		&.is-completed-or-past.abnormal-schedule {
			background: rgba(127, 29, 29, 0.3) !important;
			border: 3px solid #ef4444 !important; /* 加粗到3px，使用最亮的红色 */
			box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.5) !important;
			opacity: 1 !important;
			filter: none !important;

			&:hover {
				background: rgba(127, 29, 29, 0.4) !important;
				box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.7) !important;
			}

			/* 异常排班内部内容也不灰化 */
			.cell-content .shift-content .shift-main {
				filter: none !important;
			}
		}
	}

	/* 加载样式暗黑模式 */
	.loading-overlay {
		background: rgba(31, 31, 31, 0.9);

		.loading-spinner {
			.spinner-ring {
				border: 4px solid var(--next-border-color, #424242);
				border-top-color: #60a5fa;
			}

			.loading-text {
				color: var(--next-text-color-regular, #e5eaf3);
			}
		}
	}

	/* 批量排班面板暗黑模式 */
	.batch-panel-overlay {
		background: rgba(0, 0, 0, 0.7);
	}

	.batch-panel {
		background: var(--next-bg-main-color, #1f1f1f);
		border: 1px solid var(--next-border-color, #424242);

		.panel-header {
			background: var(--next-bg-main-color, #2a2a2a);
			border-bottom-color: var(--next-border-color, #424242);
			color: var(--next-text-color-primary, #e5eaf3);

			.close-btn {
				color: var(--next-text-color-secondary, #a3a6ad);

				&:hover {
					background: var(--next-bg-hover, #3a3a3a);
					color: var(--next-text-color-primary, #e5eaf3);
				}
			}
		}

		.panel-content {
			.selection-summary {
				background: var(--next-color-primary, #303030);
				border: 1px solid var(--next-border-color, #424242);

				.summary-item {
					.label {
						color: var(--next-text-color-placeholder, #9b9da1);
					}

					.value {
						color: var(--next-text-color-regular, #e5eaf3);
					}
				}
			}

			.form-group {
				.form-label {
					color: var(--next-text-color-regular, #e5eaf3);
				}

				.form-select {
					background: var(--next-color-primary, #303030);
					border: 1px solid var(--next-border-color, #424242);
					color: var(--next-text-color-regular, #e5eaf3);

					&:focus {
						border-color: #6366f1;
						box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
					}
				}
			}

			.warning-tips {
				background: rgba(146, 64, 14, 0.1);
				border: 1px solid var(--shift-rest-border, #d97706);

				.tip-text {
					color: #fbbf24;
				}
			}
		}

		.panel-footer {
			border-top: 1px solid var(--next-border-color, #424242);

			.btn {
				&.btn-cancel {
					background: var(--next-color-primary, #303030);
					border: 1px solid var(--next-border-color, #424242);
					color: var(--next-text-color-regular, #e5eaf3);

					&:hover {
						background: var(--next-color-hover, #3c3c3c);
					}
				}
			}
		}
	}

	/* 批量模式提示暗黑模式 */
	.batch-mode-tips {
		.tips-content {
			background: #1f2937;
			border-color: #374151;
			box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);

			.tips-header {
				background: #111827;
				border-bottom-color: #374151;

				.tips-title {
					color: #f9fafb;

					i {
						color: #60a5fa;
					}
				}

				.exit-btn {
					color: #9ca3af;

					&:hover {
						background: #374151;
						color: #f9fafb;
					}
				}
			}

			.step-tips {
				.step {
					.step-indicator {
						.step-number {
							background: #111827;
							border-color: #4b5563;
							color: #9ca3af;
						}
					}

					.step-content .step-text {
						color: #9ca3af;
					}

					&.active {
						background: #1e3a5f;

						.step-indicator .step-number {
							background: #3b82f6;
							color: #ffffff;
							border-color: #3b82f6;
						}

						.step-content .step-text {
							color: #f9fafb;
						}
					}
				}
			}

			.selection-info {
				background: #111827;
				border-color: #374151;

				.info-item {
					i {
						color: #60a5fa;
					}

					.info-content {
						.label {
							color: #9ca3af;
						}

						.value {
							color: #f9fafb;
						}
					}
				}

				.info-divider {
					background: #4b5563;
				}
			}
		}
	}
}

@keyframes tipsFadeIn {
	from {
		opacity: 0;
		transform: translateY(-10px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* ==================== 加载样式 ==================== */
.loading-overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(255, 255, 255, 0.9);
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	z-index: 1000;

	.loading-spinner {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 16px;

		.spinner-ring {
			width: 48px;
			height: 48px;
			border: 4px solid var(--calendar-border-color, #e5e7eb);
			border-top-color: #3b82f6;
			border-radius: 50%;
			animation: spin 1s linear infinite;
		}

		.loading-text {
			color: #6b7280;
			font-size: 14px;
			font-weight: 500;
		}
	}
}

@keyframes spin {
	to {
		transform: rotate(360deg);
	}
}

/* ==================== 批量排班样式 ==================== */
.batch-panel-overlay {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.5);
	z-index: 2000;
	animation: fadeIn 0.3s ease-out;
}

.batch-panel {
	position: fixed;
	z-index: 2001;
	background: var(--calendar-bg-primary, white);
	border-radius: 12px;
	box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
	border: 1px solid var(--calendar-border-color, #e5e7eb);
	min-width: 480px;
	max-width: 90vw;
	max-height: 90vh;
	overflow: hidden;
	animation: slideIn 0.3s ease-out;
	transition: box-shadow 0.2s ease;

	&.dragging {
		box-shadow: 0 12px 48px rgba(0, 0, 0, 0.25);
		cursor: move;
	}

	.panel-header {
		background: var(--calendar-bg-secondary, #f8fafc);
		border-bottom: 1px solid var(--calendar-border-color, #e5e7eb);
		color: var(--calendar-text-primary, #1f2937);
		padding: 16px 20px;
		display: flex;
		justify-content: space-between;
		align-items: center;

		&.draggable-header {
			cursor: move;
			user-select: none;
			transition: background-color 0.2s ease;

			&:hover {
				background: var(--calendar-bg-hover, #e5e7eb);
			}

			&:active {
				background: var(--calendar-border-color, #d1d5db);
			}
		}

		.header-content {
			display: flex;
			align-items: center;
			gap: 8px;

			.drag-handle {
				font-size: 16px;
				color: var(--calendar-text-secondary, #9ca3af);
				letter-spacing: -2px;
				transform: rotate(90deg);
				transition: color 0.2s ease;
			}

			h3 {
				margin: 0;
				font-size: 18px;
				font-weight: 600;
			}
		}

		&:hover .drag-handle {
			color: var(--calendar-text-primary, #374151);
		}

		.close-btn {
			background: none;
			border: none;
			color: var(--calendar-text-secondary, #6b7280);
			font-size: 20px;
			cursor: pointer;
			padding: 4px;
			width: 28px;
			height: 28px;
			border-radius: 6px;
			display: flex;
			align-items: center;
			justify-content: center;
			transition: all 0.2s ease;

			&:hover {
				background: var(--calendar-bg-hover, #e5e7eb);
				color: var(--calendar-text-primary, #374151);
			}
		}
	}

	.panel-content {
		padding: 24px;

		.batch-form {
			.form-select {
				width: 100%;
			}

			.form-input {
				width: 100%;
			}

			.form-date-picker {
				width: 100%;
			}
		}

		.selection-summary {
			background: var(--calendar-bg-secondary, #f8fafc);
			border: 1px solid var(--calendar-border-color, #e5e7eb);
			border-radius: 8px;
			padding: 16px;
			margin-bottom: 20px;

			.summary-item {
				display: flex;
				justify-content: space-between;
				margin-bottom: 8px;

				&:last-child {
					margin-bottom: 0;
				}

				.label {
					color: var(--calendar-text-secondary, #6b7280);
					font-size: 14px;
				}

				.value {
					color: var(--calendar-text-primary, #374151);
					font-weight: 600;
					font-size: 14px;
				}
			}
		}

		.panel-actions {
			background: var(--next-bg-main-color, #2a2a2a);
			border-top-color: var(--next-border-color, #424242);
		}

		.form-group {
			margin-bottom: 20px;

			.form-label {
				display: block;
				color: var(--calendar-text-primary, #374151);
				font-weight: 600;
				font-size: 14px;
				margin-bottom: 8px;
			}

			.form-select {
				width: 100%;
				padding: 12px 16px;
				border: 1px solid var(--calendar-border-color, #d1d5db);
				border-radius: 8px;
				font-size: 14px;
				background: var(--calendar-bg-primary, white);
				transition: border-color 0.2s ease;

				&:focus {
					outline: none;
					border-color: #3b82f6;
					box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
				}
			}
		}

		.warning-tips {
			background: #fffbeb;
			border: 1px solid #fed7aa;
			border-radius: 8px;
			padding: 16px;

			.tip-item {
				display: flex;
				align-items: flex-start;
				gap: 8px;
				margin-bottom: 8px;

				&:last-child {
					margin-bottom: 0;
				}

				.tip-icon {
					font-size: 16px;
					margin-top: 1px;
					color: #f59e0b;
				}

				.tip-text {
					color: #92400e;
					font-size: 13px;
					line-height: 1.4;
				}
			}
		}
	}

	.panel-actions {
		padding: 16px 24px;
		border-top: 1px solid var(--calendar-border-color, #e5e7eb);
		display: flex;
		justify-content: flex-end;
		gap: 12px;
		background: var(--calendar-bg-secondary, #f8fafc);
	}

	.panel-footer {
		padding: 16px 24px;
		border-top: 1px solid var(--calendar-border-color, #e5e7eb);
		display: flex;
		justify-content: flex-end;
		gap: 12px;

		.btn {
			padding: 10px 20px;
			border-radius: 8px;
			font-size: 14px;
			font-weight: 600;
			cursor: pointer;
			transition: all 0.2s ease;
			border: 1px solid var(--calendar-border-color, #d1d5db);

			&.btn-cancel {
				background: var(--calendar-bg-primary, white);
				color: var(--calendar-text-primary, #374151);

				&:hover {
					background: var(--calendar-bg-hover, #f9fafb);
					border-color: var(--calendar-border-color, #9ca3af);
				}
			}

			&.btn-confirm {
				background: #3b82f6;
				border-color: #3b82f6;
				color: white;

				&:hover {
					background: #2563eb;
					border-color: #2563eb;
				}

				&:disabled {
					background: #d1d5db;
					border-color: #d1d5db;
					cursor: not-allowed;
				}
			}
		}
	}
}

.batch-mode-tips {
	position: fixed;
	top: 24px; /* 放在顶部，距离顶部24px */
	left: 50%;
	transform: translateX(-50%);
	z-index: 1500;
	pointer-events: none; /* 让提示框不阻挡点击，但保留内容可交互 */

	.tips-content {
		pointer-events: auto; /* 恢复提示框内容的交互 */
		background: #ffffff;
		color: #1f2937;
		padding: 0;
		border-radius: 8px;
		box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
		border: 1px solid #e5e7eb;
		width: 300px; /* 缩小一半：原来最大600px，现在300px */
		max-width: calc(100vw - 48px); /* 自适应屏幕，左右各留24px边距 */
		min-width: 240px; /* 缩小一半：原来最小480px，现在240px */
		overflow: hidden;

		.tips-header {
			display: flex;
			align-items: center;
			justify-content: space-between;
			padding: 8px 12px; /* 缩小：原来12px 16px */
			background: #f9fafb;
			border-bottom: 1px solid #e5e7eb;

			.tips-title {
				display: flex;
				align-items: center;
				gap: 6px; /* 缩小：原来8px */
				font-size: 13px; /* 缩小：原来14px */
				font-weight: 500;
				color: #1f2937;

				i {
					font-size: 14px; /* 缩小：原来16px */
					color: #3b82f6;
				}
			}

			.exit-btn {
				color: #6b7280;
				padding: 4px;
				cursor: pointer;
				border-radius: 4px;
				transition: all 0.2s;

				&:hover {
					color: #1f2937;
					background: #e5e7eb;
				}

				i {
					font-size: 14px;
				}
			}
		}

		.step-tips {
			display: flex;
			flex-direction: column;
			gap: 0;
			padding: 12px; /* 缩小：原来16px */

			.step {
				display: flex;
				align-items: flex-start;
				gap: 8px; /* 缩小：原来10px */
				padding: 8px 10px; /* 缩小：原来10px 12px */
				border-radius: 6px;
				opacity: 0.5;
				transition: all 0.2s ease;
				margin-bottom: 4px; /* 缩小：原来6px */

				&:last-child {
					margin-bottom: 0;
				}

				.step-indicator {
					flex-shrink: 0;
					margin-top: 2px;

					.step-number {
						display: inline-flex;
						align-items: center;
						justify-content: center;
						width: 20px; /* 缩小：原来24px */
						height: 20px; /* 缩小：原来24px */
						background: #f3f4f6;
						border: 1.5px solid #d1d5db;
						border-radius: 50%;
						font-weight: 500;
						font-size: 11px; /* 缩小：原来12px */
						color: #6b7280;
						transition: all 0.2s ease;
					}

					.step-check {
						display: inline-flex;
						align-items: center;
						justify-content: center;
						width: 20px; /* 缩小：原来24px */
						height: 20px; /* 缩小：原来24px */
						background: #10b981;
						border: none;
						border-radius: 50%;
						font-size: 12px; /* 缩小：原来14px */
						color: white;
					}
				}

				.step-content {
					flex: 1;
					min-width: 0;

					.step-text {
						font-size: 12px; /* 缩小：原来13px */
						line-height: 1.4; /* 缩小：原来1.5 */
						color: #6b7280;
						font-weight: 400;
					}

					.step-detail {
						font-size: 11px; /* 缩小：原来12px */
						color: #10b981;
						margin-top: 2px;
						font-weight: 500;
					}
				}

				&.active {
					opacity: 1;
					background: #eff6ff;

					.step-indicator .step-number {
						background: #3b82f6;
						color: #ffffff;
						border-color: #3b82f6;
					}

					.step-content .step-text {
						color: #1f2937;
						font-weight: 500;
					}
				}

				&.completed {
					opacity: 0.7;
				}
			}
		}

		.selection-info {
			margin: 0 12px 12px; /* 缩小：原来16px */
			padding: 10px; /* 缩小：原来12px */
			background: #f9fafb;
			border-radius: 6px;
			display: flex;
			align-items: center;
			gap: 4px;
			border: 1px solid #e5e7eb;

			.info-item {
				flex: 1;
				display: flex;
				align-items: center;
				gap: 6px; /* 缩小：原来8px */
				font-size: 12px; /* 缩小：原来13px */

				i {
					font-size: 14px; /* 缩小：原来16px */
					color: #3b82f6;
					flex-shrink: 0;
				}

				.info-content {
					display: flex;
					flex-direction: column;
					gap: 2px;
					min-width: 0;

					.label {
						font-size: 10px; /* 缩小：原来11px */
						color: #6b7280;
						font-weight: 400;
					}

					.value {
						font-weight: 600;
						color: #1f2937;
						font-size: 12px; /* 缩小：原来14px */
						white-space: nowrap;
						overflow: hidden;
						text-overflow: ellipsis;
					}
				}
			}

			.info-divider {
				width: 1px;
				height: 28px;
				background: #d1d5db;
				flex-shrink: 0;
			}
		}
	}
}

/* 动画 */
.tips-fade-enter-active,
.tips-fade-leave-active {
	transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.tips-fade-enter-from {
	opacity: 0;
	transform: translate(-50%, 20px);
}

.tips-fade-leave-to {
	opacity: 0;
	transform: translate(-50%, 20px);
}

.info-slide-enter-active {
	animation: infoSlideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.info-slide-leave-active {
	animation: infoSlideOut 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes infoSlideIn {
	from {
		opacity: 0;
		max-height: 0;
		margin-top: 0;
		margin-bottom: 0;
	}
	to {
		opacity: 1;
		max-height: 100px;
		margin-top: 0;
		margin-bottom: 20px;
	}
}

@keyframes infoSlideOut {
	from {
		opacity: 1;
		max-height: 100px;
		margin-bottom: 20px;
	}
	to {
		opacity: 0;
		max-height: 0;
		margin-bottom: 0;
	}
}

@keyframes checkBounce {
	0% {
		transform: scale(0);
		opacity: 0;
	}
	50% {
		transform: scale(1.2);
	}
	100% {
		transform: scale(1);
		opacity: 1;
	}
}

/* 日期列快速选择对话框样式 */
:deep(.date-column-select-dialog) {
	.el-dialog__header {
		padding: 16px 20px;
		border-bottom: 1px solid #e5e7eb;

		.el-dialog__title {
			font-size: 16px;
			font-weight: 500;
			color: #1f2937;
		}
	}

	.el-dialog__body {
		padding: 20px;
	}

	.el-dialog__footer {
		padding: 12px 20px;
		border-top: 1px solid #e5e7eb;
	}
}

.date-column-select-dialog {
	.dialog-content {
		.selection-preview {
			display: flex;
			align-items: center;
			gap: 12px;
			padding: 14px;
			background: #f9fafb;
			border: 1px solid #e5e7eb;
			border-radius: 6px;
			margin-bottom: 0;

			.preview-icon {
				width: 40px;
				height: 40px;
				background: #3b82f6;
				border-radius: 6px;
				display: flex;
				align-items: center;
				justify-content: center;
				color: white;
				font-size: 20px;
				flex-shrink: 0;

				i {
					font-size: 20px;
				}
			}

			.preview-info {
				flex: 1;
				min-width: 0;

				.preview-date {
					font-size: 14px;
					font-weight: 500;
					color: #1f2937;
					margin-bottom: 4px;
					line-height: 1.4;

					strong {
						font-weight: 600;
					}
				}

				.preview-hint {
					font-size: 12px;
					color: #6b7280;
					line-height: 1.4;
				}
			}
		}

		:deep(.el-divider) {
			margin: 16px 0;
		}

		.selection-options {
			.option-title {
				font-size: 13px;
				font-weight: 500;
				color: #374151;
				margin-bottom: 12px;
			}

			.scope-radio-group {
				display: flex;
				flex-direction: column;
				gap: 10px;

				:deep(.el-radio) {
					border: 1px solid #e5e7eb;
					border-radius: 6px;
					padding: 12px;
					margin: 0;
					background: white;
					transition: all 0.2s ease;
					width: 100%;
					height: auto;
					display: flex;
					align-items: center;

					&:hover {
						border-color: #d1d5db;
						background: #f9fafb;
					}

					&.is-checked {
						border-color: #3b82f6;
						background: #eff6ff;
					}

					.el-radio__input {
						margin-right: 10px;

						&.is-checked {
							.el-radio__inner {
								background: #3b82f6;
								border-color: #3b82f6;
							}
						}

						.el-radio__inner {
							width: 16px;
							height: 16px;

							&::after {
								width: 5px;
								height: 5px;
							}
						}
					}

					.el-radio__label {
						padding: 0;
						color: #374151;
						width: 100%;
					}
				}

				.radio-content {
					width: 100%;

					.radio-label {
						font-size: 14px;
						font-weight: 500;
						color: #1f2937;
						margin-bottom: 2px;
						line-height: 1.4;
					}

					.radio-desc {
						font-size: 12px;
						color: #6b7280;
						line-height: 1.3;
					}
				}

				:deep(.el-radio.is-checked) {
					.radio-content {
						.radio-label {
							color: #1f2937;
						}

						.radio-desc {
							color: #6b7280;
						}
					}
				}
			}
		}

		.selection-warning {
			margin-top: 12px;
			border-radius: 6px;

			:deep(.el-alert__title) {
				font-size: 12px;
				line-height: 1.5;
			}
		}
	}

	.dialog-footer {
		display: flex;
		justify-content: flex-end;
		gap: 10px;

		.el-button {
			padding: 8px 16px;
			font-weight: 400;
		}
	}
}

@keyframes fadeIn {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

@keyframes slideIn {
	from {
		opacity: 0;
		transform: translateY(-10px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes slideUp {
	from {
		opacity: 0;
		transform: translateX(-50%) translateY(20px);
	}
	to {
		opacity: 1;
		transform: translateX(-50%) translateY(0);
	}
}

/* ==================== 响应式样式 ==================== */
@media (max-width: 768px) {
	.schedule-table {
		min-width: 800px;

		.schedule-header tr {
			.employee-header {
				width: 150px;
				min-width: 150px;
			}

			.date-header {
				width: 100px;
				min-width: 100px;
			}
		}

		.schedule-body .schedule-row {
			.employee-cell {
				width: 150px;
				min-width: 150px;

				.employee-card {
					padding: 8px;
					gap: 8px;

					.employee-avatar .avatar-placeholder {
						width: 32px;
						height: 32px;
						font-size: 12px;
					}

					.employee-info {
						.employee-name {
							font-size: 12px;
						}

						.employee-code {
							font-size: 10px;
						}
					}
				}
			}

			.schedule-cell {
				width: 100px;
				min-width: 100px;
				height: 50px;

				.cell-content {
					padding: 6px;

					.shift-content {
						.shift-name {
							font-size: 11px;
						}

						.shift-time {
							font-size: 9px;
						}
					}
				}
			}
		}
	}
}

// 详情卡片样式
.schedule-detail-card {
	position: fixed;
	z-index: 9999;
	max-width: 400px;
	min-width: 300px;
	border-radius: 12px !important;
	border: 1px solid var(--calendar-border-color, #e5e7eb) !important;
	box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12) !important;
	transform: translate(-50%, 0);
	will-change: left, top, transform;

	:deep(.el-card__header) {
		background: var(--calendar-bg-secondary, #f8fafc);
		border-bottom: 1px solid var(--calendar-border-color, #e5e7eb);
		padding: 16px 20px;
	}

	:deep(.el-card__body) {
		padding: 20px;
	}

	.card-header-content {
		display: flex;
		align-items: center;
		justify-content: space-between;

		.date-info {
			display: flex;
			flex-direction: column;
			align-items: flex-start;

			.date-text {
				font-size: 16px;
				font-weight: 600;
				color: var(--calendar-text-primary, #1f2937);
			}

			.weekday-text {
				font-size: 12px;
				color: var(--calendar-text-secondary, #6b7280);
				margin-top: 2px;
			}
		}
	}
}

.card-overlay {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 9998;
	background: transparent;
}

// 让班次主要信息可点击
.shift-main {
	cursor: pointer;
	padding: 2px 4px;
	border-radius: 3px;
	transition: background-color 0.2s ease;

	.shift-group {
		font-size: 11px;
		line-height: 1.2;
		text-align: center;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		max-width: 100%;
		font-weight: 500;
		color: #6b7280;
		margin-bottom: 2px;
		padding: 0 2px; /* 为标识留出一点空间 */
		opacity: 0.85;
	}

	.shift-name {
		font-size: 14px;
		line-height: 1.3;
		text-align: center;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		max-width: 100%;
		font-weight: 700;
		color: #1f2937;
		letter-spacing: -0.015em;
	}

	.shift-info {
		font-size: 13px;
		line-height: 1.2;
		text-align: center;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		max-width: 100%;
		font-weight: 600;
		color: #1f2937;
		padding: 0 2px; /* 为标识留出一点空间 */
	}

	&:hover {
		background-color: rgba(64, 158, 255, 0.1);
	}
}

// 休班样式 - 已统一到 .shift-main，此处注释
/* .rest-day {
	cursor: pointer;
	padding: 2px 4px;
	border-radius: 3px;
	transition: background-color 0.2s ease;
	text-align: center;

	.rest-text {
		font-size: 12px;
		line-height: 1.2;
	}
} */

/* 实际打卡时间样式 */
.punch-time-info {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.punch-time-item {
	display: flex;
	align-items: center;
	gap: 4px;

	.el-text {
		display: flex;
		align-items: center;
		gap: 4px;
	}

	.el-icon {
		font-size: 14px;
	}
}

// 班次类型单选按钮组样式
.shift-type-radio-group {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;

	.el-radio {
		margin-right: 0;
		margin-bottom: 8px;
		border: 1px solid var(--el-border-color);
		border-radius: 8px;
		padding: 8px 16px;
		background-color: var(--el-bg-color);
		transition: var(--el-transition-all);
		cursor: pointer;

		&:hover {
			border-color: var(--el-color-primary);
			background-color: var(--el-fill-color-light);
		}

		&.is-checked {
			border-color: #409eff;
			background-color: #409eff;
			color: #ffffff;

			.el-radio__label {
				color: #ffffff;
				font-weight: 500;
			}
		}

		.el-radio__input {
			display: none;
		}

		.el-radio__label {
			font-weight: 500;
			font-size: 14px;
			padding-left: 0;
			color: var(--el-text-color-regular);
			transition: var(--el-transition-all);
		}
	}
}
</style>
